@zjex/git-workflow 0.2.18 → 0.2.20

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 CHANGED
@@ -450,6 +450,12 @@ gw r
450
450
  | --------- | ------------------------------------------------ |
451
451
  | `gw init` | 交互式初始化配置文件(可选择全局配置或项目配置) |
452
452
 
453
+ ### 🔄 更新命令
454
+
455
+ | 命令 | 别名 | 说明 |
456
+ | ----------- | -------- | -------------------- |
457
+ | `gw update` | `gw upt` | 检查并更新到最新版本 |
458
+
453
459
  ```bash
454
460
  # 创建全局配置(推荐,一次配置所有项目生效)
455
461
  gw init
@@ -462,6 +468,49 @@ gw init
462
468
  # 配置保存到 .gwrc.json
463
469
  ```
464
470
 
471
+ #### 更新命令
472
+
473
+ ```bash
474
+ # 检查并更新到最新版本
475
+ gw update
476
+
477
+ # 或使用别名
478
+ gw upt
479
+ ```
480
+
481
+ **更新流程:**
482
+
483
+ ```bash
484
+ gw update
485
+
486
+ 🔍 检查更新...
487
+
488
+ ┌─────────────────────────────────┐
489
+ │ │
490
+ │ 🎉 发现新版本! │
491
+ │ │
492
+ │ 0.2.16 → 0.2.17 │
493
+ │ │
494
+ └─────────────────────────────────┘
495
+
496
+ ✔ 更新成功!
497
+
498
+ ┌─────────────────────────────────┐
499
+ │ │
500
+ │ ✨ 更新完成! │
501
+ │ │
502
+ │ 请重新打开终端使用新版本 │
503
+ │ │
504
+ └─────────────────────────────────┘
505
+ ```
506
+
507
+ **说明:**
508
+
509
+ - 自动检测最新版本
510
+ - 如果已是最新版本,显示提示
511
+ - 如果有新版本,自动更新
512
+ - 更新成功后提示重新打开终端
513
+
465
514
  **配置流程:**
466
515
 
467
516
  ```bash
package/dist/index.js CHANGED
@@ -1,31 +1,16 @@
1
1
  #!/usr/bin/env -S node --no-warnings=ExperimentalWarning
2
-
3
- // src/index.ts
4
- import { cac } from "cac";
5
- import { select as select8 } from "@inquirer/prompts";
6
- import { ExitPromptError } from "@inquirer/core";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
7
11
 
8
12
  // src/utils.ts
9
13
  import { execSync } from "child_process";
10
- var colors = {
11
- red: (s) => `\x1B[31m${s}\x1B[0m`,
12
- green: (s) => `\x1B[32m${s}\x1B[0m`,
13
- yellow: (s) => `\x1B[33m${s}\x1B[0m`,
14
- cyan: (s) => `\x1B[36m${s}\x1B[0m`,
15
- dim: (s) => `\x1B[2m${s}\x1B[0m`,
16
- bold: (s) => `\x1B[1m${s}\x1B[0m`,
17
- reset: "\x1B[0m"
18
- };
19
- var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
20
- var theme = {
21
- helpMode: "always",
22
- style: {
23
- keysHelpTip: (keys) => {
24
- const tips = keys.map(([key, label]) => `${key} ${label}`).join(" \u2022 ");
25
- return `\x1B[2m${tips} \u2022 Ctrl+C quit\x1B[0m`;
26
- }
27
- }
28
- };
29
14
  function exec(cmd, silent = false) {
30
15
  try {
31
16
  const options = {
@@ -66,13 +51,267 @@ function getMainBranch() {
66
51
  function divider() {
67
52
  console.log(colors.dim("\u2500".repeat(40)));
68
53
  }
54
+ var colors, TODAY, theme;
55
+ var init_utils = __esm({
56
+ "src/utils.ts"() {
57
+ "use strict";
58
+ colors = {
59
+ red: (s) => `\x1B[31m${s}\x1B[0m`,
60
+ green: (s) => `\x1B[32m${s}\x1B[0m`,
61
+ yellow: (s) => `\x1B[33m${s}\x1B[0m`,
62
+ cyan: (s) => `\x1B[36m${s}\x1B[0m`,
63
+ dim: (s) => `\x1B[2m${s}\x1B[0m`,
64
+ bold: (s) => `\x1B[1m${s}\x1B[0m`,
65
+ reset: "\x1B[0m"
66
+ };
67
+ TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
68
+ theme = {
69
+ helpMode: "always",
70
+ style: {
71
+ keysHelpTip: (keys) => {
72
+ const tips = keys.map(([key, label]) => `${key} ${label}`).join(" \u2022 ");
73
+ return `\x1B[2m${tips} \u2022 Ctrl+C quit\x1B[0m`;
74
+ }
75
+ }
76
+ };
77
+ }
78
+ });
79
+
80
+ // src/update-notifier.ts
81
+ var update_notifier_exports = {};
82
+ __export(update_notifier_exports, {
83
+ checkForUpdates: () => checkForUpdates,
84
+ clearUpdateCache: () => clearUpdateCache
85
+ });
86
+ import { execSync as execSync6 } from "child_process";
87
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, unlinkSync } from "fs";
88
+ import { homedir as homedir3 } from "os";
89
+ import { join as join3 } from "path";
90
+ import boxen from "boxen";
91
+ import { select as select7 } from "@inquirer/prompts";
92
+ import ora5 from "ora";
93
+ import semver from "semver";
94
+ async function checkForUpdates(currentVersion, packageName = "@zjex/git-workflow", interactive = false) {
95
+ try {
96
+ const cache = readCache();
97
+ const now = Date.now();
98
+ if (cache?.latestVersion && cache.checkedVersion === currentVersion) {
99
+ if (cache.lastDismiss && now - cache.lastDismiss < DISMISS_INTERVAL) {
100
+ backgroundCheck(currentVersion, packageName);
101
+ return;
102
+ }
103
+ if (semver.gt(cache.latestVersion, currentVersion)) {
104
+ if (interactive) {
105
+ const action = await showUpdateMessage(
106
+ currentVersion,
107
+ cache.latestVersion,
108
+ packageName
109
+ );
110
+ if (action === "update") {
111
+ await performUpdate(packageName);
112
+ } else if (action === "dismiss") {
113
+ writeCache({ ...cache, lastDismiss: now });
114
+ }
115
+ } else {
116
+ showSimpleNotification(currentVersion, cache.latestVersion);
117
+ }
118
+ }
119
+ }
120
+ backgroundCheck(currentVersion, packageName);
121
+ } catch (error) {
122
+ if (error?.constructor?.name === "ExitPromptError") {
123
+ throw error;
124
+ }
125
+ }
126
+ }
127
+ function backgroundCheck(currentVersion, packageName) {
128
+ const cache = readCache();
129
+ const now = Date.now();
130
+ if (cache?.lastCheck && now - cache.lastCheck < CHECK_INTERVAL) {
131
+ return;
132
+ }
133
+ Promise.resolve().then(async () => {
134
+ try {
135
+ const latestVersion = await getLatestVersion(packageName);
136
+ if (latestVersion) {
137
+ writeCache({
138
+ ...cache,
139
+ lastCheck: now,
140
+ latestVersion,
141
+ checkedVersion: currentVersion
142
+ });
143
+ }
144
+ } catch {
145
+ }
146
+ });
147
+ }
148
+ async function getLatestVersion(packageName) {
149
+ try {
150
+ const result = execSync6(`npm view ${packageName} version`, {
151
+ encoding: "utf-8",
152
+ timeout: 3e3,
153
+ stdio: ["pipe", "pipe", "ignore"]
154
+ // 忽略 stderr
155
+ });
156
+ return result.trim();
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+ function showSimpleNotification(current, latest) {
162
+ const message = `${colors.yellow("\u{1F389} \u53D1\u73B0\u65B0\u7248\u672C")} ${colors.dim(
163
+ current
164
+ )} \u2192 ${colors.green(latest)} ${colors.dim("\u8FD0\u884C")} ${colors.cyan(
165
+ "gw update"
166
+ )} ${colors.dim("\u66F4\u65B0")}`;
167
+ console.log("");
168
+ console.log(
169
+ boxen(message, {
170
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
171
+ margin: { top: 0, bottom: 1, left: 0, right: 0 },
172
+ borderStyle: "round",
173
+ borderColor: "yellow",
174
+ align: "center"
175
+ })
176
+ );
177
+ }
178
+ async function showUpdateMessage(current, latest, packageName) {
179
+ const message = [
180
+ colors.yellow(colors.bold("\u{1F389} \u53D1\u73B0\u65B0\u7248\u672C\uFF01")),
181
+ "",
182
+ `${colors.dim(current)} \u2192 ${colors.green(colors.bold(latest))}`
183
+ ].join("\n");
184
+ console.log("");
185
+ console.log(
186
+ boxen(message, {
187
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
188
+ margin: 1,
189
+ borderStyle: "round",
190
+ borderColor: "yellow",
191
+ align: "center",
192
+ width: 40
193
+ })
194
+ );
195
+ try {
196
+ const action = await select7({
197
+ message: "\u4F60\u60F3\u505A\u4EC0\u4E48\uFF1F",
198
+ choices: [
199
+ {
200
+ name: "\u{1F680} \u7ACB\u5373\u66F4\u65B0",
201
+ value: "update",
202
+ description: `\u8FD0\u884C npm install -g ${packageName}`
203
+ },
204
+ {
205
+ name: "\u23ED\uFE0F \u7A0D\u540E\u66F4\u65B0\uFF0C\u7EE7\u7EED\u4F7F\u7528",
206
+ value: "continue",
207
+ description: "\u4E0B\u6B21\u542F\u52A8\u65F6\u4F1A\u518D\u6B21\u63D0\u793A"
208
+ },
209
+ {
210
+ name: "\u{1F648} \u8DF3\u8FC7\u6B64\u7248\u672C (24h \u5185\u4E0D\u518D\u63D0\u793A)",
211
+ value: "dismiss",
212
+ description: "24 \u5C0F\u65F6\u5185\u4E0D\u4F1A\u518D\u63D0\u793A\u6B64\u7248\u672C"
213
+ }
214
+ ]
215
+ });
216
+ return action;
217
+ } catch (error) {
218
+ console.log("");
219
+ throw error;
220
+ }
221
+ }
222
+ async function performUpdate(packageName) {
223
+ console.log("");
224
+ const spinner = ora5({
225
+ text: "\u6B63\u5728\u66F4\u65B0...",
226
+ spinner: "dots"
227
+ }).start();
228
+ try {
229
+ execSync6(`npm install -g ${packageName}@latest`, {
230
+ encoding: "utf-8",
231
+ stdio: ["pipe", "pipe", "pipe"]
232
+ });
233
+ spinner.succeed(colors.green("\u66F4\u65B0\u6210\u529F\uFF01"));
234
+ clearUpdateCache();
235
+ console.log("");
236
+ console.log(
237
+ boxen(
238
+ [
239
+ colors.green(colors.bold("\u2728 \u66F4\u65B0\u5B8C\u6210\uFF01")),
240
+ "",
241
+ colors.dim("\u8BF7\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u4F7F\u7528\u65B0\u7248\u672C")
242
+ ].join("\n"),
243
+ {
244
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
245
+ margin: { top: 0, bottom: 1, left: 2, right: 2 },
246
+ borderStyle: "round",
247
+ borderColor: "green",
248
+ align: "center",
249
+ width: 40
250
+ }
251
+ )
252
+ );
253
+ process.exit(0);
254
+ } catch (error) {
255
+ spinner.fail(colors.red("\u66F4\u65B0\u5931\u8D25"));
256
+ console.log("");
257
+ console.log(colors.dim(" \u4F60\u53EF\u4EE5\u624B\u52A8\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0:"));
258
+ console.log(colors.cyan(` npm install -g ${packageName}@latest`));
259
+ console.log("");
260
+ }
261
+ }
262
+ function clearUpdateCache() {
263
+ try {
264
+ const cacheFile = join3(homedir3(), CACHE_FILE);
265
+ if (existsSync3(cacheFile)) {
266
+ unlinkSync(cacheFile);
267
+ }
268
+ } catch {
269
+ }
270
+ }
271
+ function readCache() {
272
+ try {
273
+ const cacheFile = join3(homedir3(), CACHE_FILE);
274
+ if (!existsSync3(cacheFile)) {
275
+ return null;
276
+ }
277
+ const content = readFileSync3(cacheFile, "utf-8");
278
+ return JSON.parse(content);
279
+ } catch {
280
+ return null;
281
+ }
282
+ }
283
+ function writeCache(cache) {
284
+ try {
285
+ const cacheFile = join3(homedir3(), CACHE_FILE);
286
+ writeFileSync3(cacheFile, JSON.stringify(cache), "utf-8");
287
+ } catch {
288
+ }
289
+ }
290
+ var CHECK_INTERVAL, DISMISS_INTERVAL, CACHE_FILE;
291
+ var init_update_notifier = __esm({
292
+ "src/update-notifier.ts"() {
293
+ "use strict";
294
+ init_utils();
295
+ CHECK_INTERVAL = 1e3 * 60 * 60 * 4;
296
+ DISMISS_INTERVAL = 1e3 * 60 * 60 * 24;
297
+ CACHE_FILE = ".gw-update-check";
298
+ }
299
+ });
300
+
301
+ // src/index.ts
302
+ init_utils();
303
+ import { cac } from "cac";
304
+ import { select as select8 } from "@inquirer/prompts";
305
+ import { ExitPromptError } from "@inquirer/core";
69
306
 
70
307
  // src/commands/branch.ts
308
+ init_utils();
71
309
  import { execSync as execSync2 } from "child_process";
72
310
  import { select, input } from "@inquirer/prompts";
73
311
  import ora from "ora";
74
312
 
75
313
  // src/config.ts
314
+ init_utils();
76
315
  import { existsSync, readFileSync } from "fs";
77
316
  import { join } from "path";
78
317
  import { homedir } from "os";
@@ -392,6 +631,7 @@ async function deleteBranch(branchArg) {
392
631
  }
393
632
 
394
633
  // src/commands/tag.ts
634
+ init_utils();
395
635
  import { execSync as execSync3 } from "child_process";
396
636
  import { select as select2, input as input2 } from "@inquirer/prompts";
397
637
  import ora2 from "ora";
@@ -790,6 +1030,7 @@ async function updateTag() {
790
1030
  }
791
1031
 
792
1032
  // src/commands/release.ts
1033
+ init_utils();
793
1034
  import { readFileSync as readFileSync2, writeFileSync } from "fs";
794
1035
  import { select as select3 } from "@inquirer/prompts";
795
1036
  function getPackageVersion() {
@@ -888,6 +1129,7 @@ async function release() {
888
1129
  }
889
1130
 
890
1131
  // src/commands/init.ts
1132
+ init_utils();
891
1133
  import { existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
892
1134
  import { join as join2 } from "path";
893
1135
  import { homedir as homedir2 } from "os";
@@ -1172,6 +1414,7 @@ async function init() {
1172
1414
  }
1173
1415
 
1174
1416
  // src/commands/stash.ts
1417
+ init_utils();
1175
1418
  import { execSync as execSync4 } from "child_process";
1176
1419
  import { select as select5, input as input4 } from "@inquirer/prompts";
1177
1420
  import ora3 from "ora";
@@ -1406,11 +1649,13 @@ async function dropStash(index) {
1406
1649
  }
1407
1650
 
1408
1651
  // src/commands/commit.ts
1652
+ init_utils();
1409
1653
  import { execSync as execSync5 } from "child_process";
1410
1654
  import { select as select6, input as input5, checkbox } from "@inquirer/prompts";
1411
1655
  import ora4 from "ora";
1412
1656
 
1413
1657
  // src/ai-service.ts
1658
+ init_utils();
1414
1659
  var AI_PROVIDERS = {
1415
1660
  github: {
1416
1661
  name: "GitHub Models",
@@ -1890,6 +2135,7 @@ ${issues}`;
1890
2135
  }
1891
2136
 
1892
2137
  // src/commands/help.ts
2138
+ init_utils();
1893
2139
  function showHelp() {
1894
2140
  return `
1895
2141
  \u5206\u652F\u547D\u4EE4:
@@ -1928,6 +2174,13 @@ Tag \u547D\u4EE4:
1928
2174
  \u2022 \u9879\u76EE\u914D\u7F6E: .gwrc.json (\u4EC5\u5F53\u524D\u9879\u76EE)
1929
2175
  \u8FD0\u884C\u65F6\u53EF\u9009\u62E9\u914D\u7F6E\u8303\u56F4
1930
2176
 
2177
+ \u66F4\u65B0\u547D\u4EE4:
2178
+ gw update \u68C0\u67E5\u5E76\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C
2179
+ gw upt \u540C\u4E0A (\u522B\u540D)
2180
+
2181
+ \u6E05\u7406\u547D\u4EE4:
2182
+ gw clean \u6E05\u7406\u7F13\u5B58\u6587\u4EF6
2183
+
1931
2184
  Stash \u547D\u4EE4:
1932
2185
  gw stash \u4EA4\u4E92\u5F0F\u7BA1\u7406 stash
1933
2186
  gw s \u540C\u4E0A (\u522B\u540D)
@@ -1958,124 +2211,121 @@ Commit \u547D\u4EE4:
1958
2211
  `;
1959
2212
  }
1960
2213
 
1961
- // src/update-notifier.ts
1962
- import { execSync as execSync6 } from "child_process";
1963
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
1964
- import { homedir as homedir3 } from "os";
1965
- import { join as join3 } from "path";
1966
- import boxen from "boxen";
1967
- import { select as select7 } from "@inquirer/prompts";
1968
- import ora5 from "ora";
1969
- var DISMISS_INTERVAL = 1e3 * 60 * 60 * 24;
1970
- var CACHE_FILE = ".gw-update-check";
1971
- async function checkForUpdates(currentVersion, packageName = "@zjex/git-workflow") {
2214
+ // src/index.ts
2215
+ init_update_notifier();
2216
+
2217
+ // src/commands/update.ts
2218
+ init_utils();
2219
+ import { execSync as execSync7 } from "child_process";
2220
+ import ora6 from "ora";
2221
+ import boxen2 from "boxen";
2222
+ import semver2 from "semver";
2223
+ import { existsSync as existsSync4, unlinkSync as unlinkSync2 } from "fs";
2224
+ import { homedir as homedir4 } from "os";
2225
+ import { join as join4 } from "path";
2226
+ var CACHE_FILE2 = ".gw-update-check";
2227
+ function clearUpdateCache2() {
1972
2228
  try {
1973
- const cache = readCache();
1974
- const now = Date.now();
1975
- if (cache?.lastDismiss && now - cache.lastDismiss < DISMISS_INTERVAL) {
1976
- return;
1977
- }
1978
- const latestVersion = await getLatestVersion(packageName);
1979
- if (latestVersion && latestVersion !== currentVersion) {
1980
- const action = await showUpdateMessage(
1981
- currentVersion,
1982
- latestVersion,
1983
- packageName
1984
- );
1985
- if (action === "update") {
1986
- await performUpdate(packageName);
1987
- } else if (action === "dismiss") {
1988
- writeCache({ lastDismiss: now, latestVersion });
1989
- }
1990
- }
1991
- } catch (error) {
1992
- if (error?.constructor?.name === "ExitPromptError") {
1993
- throw error;
2229
+ const cacheFile = join4(homedir4(), CACHE_FILE2);
2230
+ if (existsSync4(cacheFile)) {
2231
+ unlinkSync2(cacheFile);
1994
2232
  }
2233
+ } catch {
1995
2234
  }
1996
2235
  }
1997
- async function getLatestVersion(packageName) {
2236
+ async function getLatestVersion2(packageName) {
1998
2237
  try {
1999
- const result = execSync6(`npm view ${packageName} version`, {
2238
+ const result = execSync7(`npm view ${packageName} version`, {
2000
2239
  encoding: "utf-8",
2001
2240
  timeout: 3e3,
2002
2241
  stdio: ["pipe", "pipe", "ignore"]
2003
- // 忽略 stderr
2004
2242
  });
2005
2243
  return result.trim();
2006
2244
  } catch {
2007
2245
  return null;
2008
2246
  }
2009
2247
  }
2010
- async function showUpdateMessage(current, latest, packageName) {
2011
- const message = [
2012
- colors.bold("\uFFFD \u53D1\u73B0\u65B0\u7248\u65B0\u672C\u53EF\u7528\uFF01"),
2013
- "",
2014
- `${colors.dim(current)} \u2192 ${colors.green(colors.bold(latest))}`
2015
- ].join("\n");
2248
+ async function update(currentVersion) {
2249
+ const packageName = "@zjex/git-workflow";
2016
2250
  console.log("");
2017
- console.log(
2018
- boxen(message, {
2019
- padding: 1,
2020
- margin: 1,
2021
- borderStyle: "round",
2022
- borderColor: "yellow",
2023
- align: "left"
2024
- })
2025
- );
2251
+ console.log(colors.bold("\u{1F50D} \u68C0\u67E5\u66F4\u65B0..."));
2252
+ console.log("");
2253
+ const spinner = ora6("\u6B63\u5728\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F...").start();
2026
2254
  try {
2027
- const action = await select7({
2028
- message: "\u4F60\u60F3\u505A\u4EC0\u4E48\uFF1F",
2029
- choices: [
2030
- {
2031
- name: "\u{1F680} \u7ACB\u5373\u66F4\u65B0",
2032
- value: "update",
2033
- description: `\u8FD0\u884C npm install -g ${packageName}`
2034
- },
2035
- {
2036
- name: "\u23ED\uFE0F \u7A0D\u540E\u66F4\u65B0\uFF0C\u7EE7\u7EED\u4F7F\u7528",
2037
- value: "continue",
2038
- description: "\u4E0B\u6B21\u542F\u52A8\u65F6\u4F1A\u518D\u6B21\u63D0\u793A"
2039
- },
2255
+ const latestVersion = await getLatestVersion2(packageName);
2256
+ if (!latestVersion) {
2257
+ spinner.fail("\u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F");
2258
+ console.log(colors.dim(" \u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u540E\u91CD\u8BD5"));
2259
+ return;
2260
+ }
2261
+ spinner.stop();
2262
+ if (semver2.gte(currentVersion, latestVersion)) {
2263
+ console.log(
2264
+ boxen2(
2265
+ [
2266
+ colors.green(colors.bold("\u2705 \u5DF2\u662F\u6700\u65B0\u7248\u672C")),
2267
+ "",
2268
+ `\u5F53\u524D\u7248\u672C: ${colors.green(currentVersion)}`
2269
+ ].join("\n"),
2270
+ {
2271
+ padding: 1,
2272
+ margin: { top: 0, bottom: 1, left: 2, right: 2 },
2273
+ borderStyle: "round",
2274
+ borderColor: "green",
2275
+ align: "left"
2276
+ }
2277
+ )
2278
+ );
2279
+ return;
2280
+ }
2281
+ const versionText = `${currentVersion} \u2192 ${latestVersion}`;
2282
+ const maxWidth = Math.max(
2283
+ "\u{1F389} \u53D1\u73B0\u65B0\u7248\u672C\uFF01".length,
2284
+ versionText.length,
2285
+ "\u2728 \u66F4\u65B0\u5B8C\u6210\uFF01".length,
2286
+ "\u8BF7\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u4F7F\u7528\u65B0\u7248\u672C".length
2287
+ );
2288
+ console.log(
2289
+ boxen2(
2290
+ [
2291
+ colors.yellow(colors.bold("\u{1F389} \u53D1\u73B0\u65B0\u7248\u672C\uFF01")),
2292
+ "",
2293
+ `${colors.dim(currentVersion)} \u2192 ${colors.green(
2294
+ colors.bold(latestVersion)
2295
+ )}`
2296
+ ].join("\n"),
2040
2297
  {
2041
- name: "\u{1F648} \u8DF3\u8FC7\u6B64\u7248\u672C (24h \u5185\u4E0D\u518D\u63D0\u793A)",
2042
- value: "dismiss",
2043
- description: "24 \u5C0F\u65F6\u5185\u4E0D\u4F1A\u518D\u63D0\u793A\u6B64\u7248\u672C"
2298
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
2299
+ margin: { top: 0, bottom: 1, left: 2, right: 2 },
2300
+ borderStyle: "round",
2301
+ borderColor: "yellow",
2302
+ align: "center",
2303
+ width: 40
2044
2304
  }
2045
- ]
2046
- });
2047
- return action;
2048
- } catch (error) {
2049
- console.log("");
2050
- throw error;
2051
- }
2052
- }
2053
- async function performUpdate(packageName) {
2054
- console.log("");
2055
- const spinner = ora5({
2056
- text: "\u6B63\u5728\u66F4\u65B0...",
2057
- spinner: "dots"
2058
- }).start();
2059
- try {
2060
- execSync6(`npm install -g ${packageName}@latest`, {
2305
+ )
2306
+ );
2307
+ const updateSpinner = ora6("\u6B63\u5728\u66F4\u65B0...").start();
2308
+ execSync7(`npm install -g ${packageName}@latest`, {
2061
2309
  encoding: "utf-8",
2062
2310
  stdio: ["pipe", "pipe", "pipe"]
2063
2311
  });
2064
- spinner.succeed(colors.green("\u66F4\u65B0\u6210\u529F\uFF01"));
2312
+ updateSpinner.succeed(colors.green("\u66F4\u65B0\u6210\u529F\uFF01"));
2313
+ clearUpdateCache2();
2065
2314
  console.log("");
2066
2315
  console.log(
2067
- boxen(
2316
+ boxen2(
2068
2317
  [
2069
- colors.bold("\u2728 \u66F4\u65B0\u5B8C\u6210\uFF01"),
2318
+ colors.green(colors.bold("\u2728 \u66F4\u65B0\u5B8C\u6210\uFF01")),
2070
2319
  "",
2071
2320
  colors.dim("\u8BF7\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u4F7F\u7528\u65B0\u7248\u672C")
2072
2321
  ].join("\n"),
2073
2322
  {
2074
- padding: 1,
2323
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
2075
2324
  margin: { top: 0, bottom: 1, left: 2, right: 2 },
2076
2325
  borderStyle: "round",
2077
2326
  borderColor: "green",
2078
- align: "left"
2327
+ align: "center",
2328
+ width: 40
2079
2329
  }
2080
2330
  )
2081
2331
  );
@@ -2086,25 +2336,7 @@ async function performUpdate(packageName) {
2086
2336
  console.log(colors.dim(" \u4F60\u53EF\u4EE5\u624B\u52A8\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0:"));
2087
2337
  console.log(colors.cyan(` npm install -g ${packageName}@latest`));
2088
2338
  console.log("");
2089
- }
2090
- }
2091
- function readCache() {
2092
- try {
2093
- const cacheFile = join3(homedir3(), CACHE_FILE);
2094
- if (!existsSync3(cacheFile)) {
2095
- return null;
2096
- }
2097
- const content = readFileSync3(cacheFile, "utf-8");
2098
- return JSON.parse(content);
2099
- } catch {
2100
- return null;
2101
- }
2102
- }
2103
- function writeCache(cache) {
2104
- try {
2105
- const cacheFile = join3(homedir3(), CACHE_FILE);
2106
- writeFileSync3(cacheFile, JSON.stringify(cache), "utf-8");
2107
- } catch {
2339
+ process.exit(1);
2108
2340
  }
2109
2341
  }
2110
2342
 
@@ -2133,7 +2365,7 @@ process.on("SIGTERM", () => {
2133
2365
  console.log("");
2134
2366
  process.exit(0);
2135
2367
  });
2136
- var version = true ? "0.2.18" : "0.0.0-dev";
2368
+ var version = true ? "0.2.20" : "0.0.0-dev";
2137
2369
  async function mainMenu() {
2138
2370
  console.log(
2139
2371
  colors.green(`
@@ -2252,7 +2484,7 @@ async function mainMenu() {
2252
2484
  }
2253
2485
  var cli = cac("gw");
2254
2486
  cli.command("", "\u663E\u793A\u4EA4\u4E92\u5F0F\u83DC\u5355").action(async () => {
2255
- await checkForUpdates(version, "@zjex/git-workflow");
2487
+ await checkForUpdates(version, "@zjex/git-workflow", true);
2256
2488
  return mainMenu();
2257
2489
  });
2258
2490
  cli.command("feature", "\u521B\u5EFA feature \u5206\u652F").alias("feat").alias("f").option("--base <branch>", "\u6307\u5B9A\u57FA\u7840\u5206\u652F").action(async (options) => {
@@ -2308,6 +2540,16 @@ cli.command("commit", "\u4EA4\u4E92\u5F0F\u63D0\u4EA4 (Conventional Commits + Gi
2308
2540
  checkGitRepo();
2309
2541
  return commit();
2310
2542
  });
2543
+ cli.command("update", "\u68C0\u67E5\u5E76\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C").alias("upt").action(async () => {
2544
+ return update(version);
2545
+ });
2546
+ cli.command("clean", "\u6E05\u7406\u7F13\u5B58\u6587\u4EF6").action(async () => {
2547
+ const { clearUpdateCache: clearUpdateCache3 } = await Promise.resolve().then(() => (init_update_notifier(), update_notifier_exports));
2548
+ clearUpdateCache3();
2549
+ console.log("");
2550
+ console.log(colors.green("\u2714 \u7F13\u5B58\u5DF2\u6E05\u7406"));
2551
+ console.log("");
2552
+ });
2311
2553
  cli.help((sections) => {
2312
2554
  sections.push({
2313
2555
  body: showHelp()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zjex/git-workflow",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "description": "🚀 极简的 Git 工作流 CLI 工具,让分支管理和版本发布变得轻松愉快",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,10 +42,12 @@
42
42
  "@inquirer/prompts": "^7.0.0",
43
43
  "boxen": "^8.0.1",
44
44
  "cac": "^6.7.14",
45
- "ora": "^9.0.0"
45
+ "ora": "^9.0.0",
46
+ "semver": "^7.7.3"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@types/node": "^25.0.3",
50
+ "@types/semver": "^7.7.1",
49
51
  "changelogen": "^0.6.2",
50
52
  "husky": "^9.1.7",
51
53
  "tsup": "^8.5.1",
@@ -38,6 +38,13 @@ Tag 命令:
38
38
  • 项目配置: .gwrc.json (仅当前项目)
39
39
  运行时可选择配置范围
40
40
 
41
+ 更新命令:
42
+ gw update 检查并更新到最新版本
43
+ gw upt 同上 (别名)
44
+
45
+ 清理命令:
46
+ gw clean 清理缓存文件
47
+
41
48
  Stash 命令:
42
49
  gw stash 交互式管理 stash
43
50
  gw s 同上 (别名)
@@ -0,0 +1,157 @@
1
+ import { execSync } from "child_process";
2
+ import ora from "ora";
3
+ import boxen from "boxen";
4
+ import semver from "semver";
5
+ import { existsSync, unlinkSync } from "fs";
6
+ import { homedir } from "os";
7
+ import { join } from "path";
8
+ import { colors } from "../utils.js";
9
+
10
+ const CACHE_FILE = ".gw-update-check";
11
+
12
+ /**
13
+ * 清理更新缓存文件
14
+ */
15
+ function clearUpdateCache(): void {
16
+ try {
17
+ const cacheFile = join(homedir(), CACHE_FILE);
18
+ if (existsSync(cacheFile)) {
19
+ unlinkSync(cacheFile);
20
+ }
21
+ } catch {
22
+ // 静默失败
23
+ }
24
+ }
25
+
26
+ /**
27
+ * 获取 npm 上的最新版本
28
+ */
29
+ async function getLatestVersion(packageName: string): Promise<string | null> {
30
+ try {
31
+ const result = execSync(`npm view ${packageName} version`, {
32
+ encoding: "utf-8",
33
+ timeout: 3000,
34
+ stdio: ["pipe", "pipe", "ignore"],
35
+ });
36
+ return result.trim();
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * 手动更新命令
44
+ */
45
+ export async function update(currentVersion: string): Promise<void> {
46
+ const packageName = "@zjex/git-workflow";
47
+
48
+ console.log("");
49
+ console.log(colors.bold("🔍 检查更新..."));
50
+ console.log("");
51
+
52
+ const spinner = ora("正在获取最新版本信息...").start();
53
+
54
+ try {
55
+ const latestVersion = await getLatestVersion(packageName);
56
+
57
+ if (!latestVersion) {
58
+ spinner.fail("无法获取最新版本信息");
59
+ console.log(colors.dim(" 请检查网络连接后重试"));
60
+ return;
61
+ }
62
+
63
+ spinner.stop();
64
+
65
+ // 使用 semver 比较版本
66
+ if (semver.gte(currentVersion, latestVersion)) {
67
+ console.log(
68
+ boxen(
69
+ [
70
+ colors.green(colors.bold("✅ 已是最新版本")),
71
+ "",
72
+ `当前版本: ${colors.green(currentVersion)}`,
73
+ ].join("\n"),
74
+ {
75
+ padding: 1,
76
+ margin: { top: 0, bottom: 1, left: 2, right: 2 },
77
+ borderStyle: "round",
78
+ borderColor: "green",
79
+ align: "left",
80
+ }
81
+ )
82
+ );
83
+ return;
84
+ }
85
+
86
+ // 有新版本
87
+ const versionText = `${currentVersion} → ${latestVersion}`;
88
+ const maxWidth = Math.max(
89
+ "🎉 发现新版本!".length,
90
+ versionText.length,
91
+ "✨ 更新完成!".length,
92
+ "请重新打开终端使用新版本".length
93
+ );
94
+
95
+ console.log(
96
+ boxen(
97
+ [
98
+ colors.yellow(colors.bold("🎉 发现新版本!")),
99
+ "",
100
+ `${colors.dim(currentVersion)} → ${colors.green(
101
+ colors.bold(latestVersion)
102
+ )}`,
103
+ ].join("\n"),
104
+ {
105
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
106
+ margin: { top: 0, bottom: 1, left: 2, right: 2 },
107
+ borderStyle: "round",
108
+ borderColor: "yellow",
109
+ align: "center",
110
+ width: 40,
111
+ }
112
+ )
113
+ );
114
+
115
+ // 开始更新
116
+ const updateSpinner = ora("正在更新...").start();
117
+
118
+ execSync(`npm install -g ${packageName}@latest`, {
119
+ encoding: "utf-8",
120
+ stdio: ["pipe", "pipe", "pipe"],
121
+ });
122
+
123
+ updateSpinner.succeed(colors.green("更新成功!"));
124
+
125
+ // 清理缓存文件
126
+ clearUpdateCache();
127
+
128
+ console.log("");
129
+ console.log(
130
+ boxen(
131
+ [
132
+ colors.green(colors.bold("✨ 更新完成!")),
133
+ "",
134
+ colors.dim("请重新打开终端使用新版本"),
135
+ ].join("\n"),
136
+ {
137
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
138
+ margin: { top: 0, bottom: 1, left: 2, right: 2 },
139
+ borderStyle: "round",
140
+ borderColor: "green",
141
+ align: "center",
142
+ width: 40,
143
+ }
144
+ )
145
+ );
146
+
147
+ // 更新成功后退出
148
+ process.exit(0);
149
+ } catch (error) {
150
+ spinner.fail(colors.red("更新失败"));
151
+ console.log("");
152
+ console.log(colors.dim(" 你可以手动运行以下命令更新:"));
153
+ console.log(colors.cyan(` npm install -g ${packageName}@latest`));
154
+ console.log("");
155
+ process.exit(1);
156
+ }
157
+ }
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ import { stash } from "./commands/stash.js";
12
12
  import { commit } from "./commands/commit.js";
13
13
  import { showHelp } from "./commands/help.js";
14
14
  import { checkForUpdates } from "./update-notifier.js";
15
+ import { update } from "./commands/update.js";
15
16
 
16
17
  // 捕获 Ctrl+C 退出,静默处理
17
18
  process.on("uncaughtException", (err) => {
@@ -177,7 +178,7 @@ const cli = cac("gw");
177
178
 
178
179
  // 默认命令 - 显示交互式菜单
179
180
  cli.command("", "显示交互式菜单").action(async () => {
180
- await checkForUpdates(version, "@zjex/git-workflow");
181
+ await checkForUpdates(version, "@zjex/git-workflow", true);
181
182
  return mainMenu();
182
183
  });
183
184
 
@@ -282,6 +283,21 @@ cli
282
283
  return commit();
283
284
  });
284
285
 
286
+ cli
287
+ .command("update", "检查并更新到最新版本")
288
+ .alias("upt")
289
+ .action(async () => {
290
+ return update(version);
291
+ });
292
+
293
+ cli.command("clean", "清理缓存文件").action(async () => {
294
+ const { clearUpdateCache } = await import("./update-notifier.js");
295
+ clearUpdateCache();
296
+ console.log("");
297
+ console.log(colors.green("✔ 缓存已清理"));
298
+ console.log("");
299
+ });
300
+
285
301
  cli.help((sections) => {
286
302
  sections.push({
287
303
  body: showHelp(),
@@ -1,57 +1,72 @@
1
1
  import { execSync } from "child_process";
2
- import { readFileSync, writeFileSync, existsSync } from "fs";
2
+ import { readFileSync, writeFileSync, existsSync, unlinkSync } from "fs";
3
3
  import { homedir } from "os";
4
4
  import { join } from "path";
5
5
  import boxen from "boxen";
6
6
  import { select } from "@inquirer/prompts";
7
7
  import ora from "ora";
8
+ import semver from "semver";
8
9
  import { colors } from "./utils.js";
9
10
 
11
+ const CHECK_INTERVAL = 1000 * 60 * 60 * 4; // 4 小时检查一次
10
12
  const DISMISS_INTERVAL = 1000 * 60 * 60 * 24; // 24 小时后再次提示
11
13
  const CACHE_FILE = ".gw-update-check";
12
14
 
13
15
  interface UpdateCache {
16
+ lastCheck?: number; // 上次检查更新的时间
14
17
  lastDismiss?: number; // 用户上次关闭提示的时间
15
- latestVersion?: string;
18
+ latestVersion?: string; // 最新版本号
19
+ checkedVersion?: string; // 检查时的当前版本
16
20
  }
17
21
 
18
22
  /**
19
- * 检查是否有新版本
23
+ * 检查是否有新版本(异步静默检查)
24
+ * @param currentVersion 当前版本
25
+ * @param packageName 包名
26
+ * @param interactive 是否交互式(true: 显示完整提示并可选择更新,false: 只显示简单提示)
20
27
  */
21
28
  export async function checkForUpdates(
22
29
  currentVersion: string,
23
- packageName: string = "@zjex/git-workflow"
30
+ packageName: string = "@zjex/git-workflow",
31
+ interactive: boolean = false
24
32
  ): Promise<void> {
25
33
  try {
26
- // 读取缓存
27
34
  const cache = readCache();
28
35
  const now = Date.now();
29
36
 
30
- // 如果用户在 24 小时内关闭过提示,跳过
31
- if (cache?.lastDismiss && now - cache.lastDismiss < DISMISS_INTERVAL) {
32
- return;
33
- }
37
+ // 1. 先检查缓存中是否有新版本需要提示
38
+ if (cache?.latestVersion && cache.checkedVersion === currentVersion) {
39
+ // 如果用户在 24 小时内关闭过提示,跳过
40
+ if (cache.lastDismiss && now - cache.lastDismiss < DISMISS_INTERVAL) {
41
+ // 继续后台检查(不阻塞)
42
+ backgroundCheck(currentVersion, packageName);
43
+ return;
44
+ }
45
+
46
+ // 使用 semver 比较版本
47
+ if (semver.gt(cache.latestVersion, currentVersion)) {
48
+ if (interactive) {
49
+ // 交互式模式:显示完整提示,可选择更新
50
+ const action = await showUpdateMessage(
51
+ currentVersion,
52
+ cache.latestVersion,
53
+ packageName
54
+ );
34
55
 
35
- // 获取最新版本
36
- const latestVersion = await getLatestVersion(packageName);
37
-
38
- // 如果有新版本,显示提示
39
- if (latestVersion && latestVersion !== currentVersion) {
40
- const action = await showUpdateMessage(
41
- currentVersion,
42
- latestVersion,
43
- packageName
44
- );
45
-
46
- if (action === "update") {
47
- // 用户选择立即更新
48
- await performUpdate(packageName);
49
- } else if (action === "dismiss") {
50
- // 用户选择跳过,记录时间
51
- writeCache({ lastDismiss: now, latestVersion });
56
+ if (action === "update") {
57
+ await performUpdate(packageName);
58
+ } else if (action === "dismiss") {
59
+ writeCache({ ...cache, lastDismiss: now });
60
+ }
61
+ } else {
62
+ // 非交互式模式:只显示简单提示
63
+ showSimpleNotification(currentVersion, cache.latestVersion);
64
+ }
52
65
  }
53
- // action === "continue" 时直接继续,不记录
54
66
  }
67
+
68
+ // 2. 后台异步检查更新(不阻塞当前命令)
69
+ backgroundCheck(currentVersion, packageName);
55
70
  } catch (error) {
56
71
  // 如果是用户按 Ctrl+C,重新抛出让全局处理
57
72
  if (error?.constructor?.name === "ExitPromptError") {
@@ -61,6 +76,37 @@ export async function checkForUpdates(
61
76
  }
62
77
  }
63
78
 
79
+ /**
80
+ * 后台异步检查更新(不阻塞)
81
+ */
82
+ function backgroundCheck(currentVersion: string, packageName: string): void {
83
+ const cache = readCache();
84
+ const now = Date.now();
85
+
86
+ // 如果距离上次检查不到 4 小时,跳过
87
+ if (cache?.lastCheck && now - cache.lastCheck < CHECK_INTERVAL) {
88
+ return;
89
+ }
90
+
91
+ // 使用 Promise 异步执行,不阻塞当前命令
92
+ Promise.resolve().then(async () => {
93
+ try {
94
+ const latestVersion = await getLatestVersion(packageName);
95
+
96
+ if (latestVersion) {
97
+ writeCache({
98
+ ...cache,
99
+ lastCheck: now,
100
+ latestVersion,
101
+ checkedVersion: currentVersion,
102
+ });
103
+ }
104
+ } catch {
105
+ // 静默失败
106
+ }
107
+ });
108
+ }
109
+
64
110
  /**
65
111
  * 获取 npm 上的最新版本
66
112
  */
@@ -78,7 +124,29 @@ async function getLatestVersion(packageName: string): Promise<string | null> {
78
124
  }
79
125
 
80
126
  /**
81
- * 显示更新提示消息并让用户选择
127
+ * 显示简单的更新通知(非交互式,不阻塞)
128
+ */
129
+ function showSimpleNotification(current: string, latest: string): void {
130
+ const message = `${colors.yellow("🎉 发现新版本")} ${colors.dim(
131
+ current
132
+ )} → ${colors.green(latest)} ${colors.dim("运行")} ${colors.cyan(
133
+ "gw update"
134
+ )} ${colors.dim("更新")}`;
135
+
136
+ console.log("");
137
+ console.log(
138
+ boxen(message, {
139
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
140
+ margin: { top: 0, bottom: 1, left: 0, right: 0 },
141
+ borderStyle: "round",
142
+ borderColor: "yellow",
143
+ align: "center",
144
+ })
145
+ );
146
+ }
147
+
148
+ /**
149
+ * 显示更新提示消息并让用户选择(交互式)
82
150
  * @returns "update" | "continue" | "dismiss"
83
151
  */
84
152
  async function showUpdateMessage(
@@ -87,7 +155,7 @@ async function showUpdateMessage(
87
155
  packageName: string
88
156
  ): Promise<"update" | "continue" | "dismiss"> {
89
157
  const message = [
90
- colors.bold(" 发现新版新本可用!"),
158
+ colors.yellow(colors.bold("🎉 发现新版本!")),
91
159
  "",
92
160
  `${colors.dim(current)} → ${colors.green(colors.bold(latest))}`,
93
161
  ].join("\n");
@@ -95,11 +163,12 @@ async function showUpdateMessage(
95
163
  console.log("");
96
164
  console.log(
97
165
  boxen(message, {
98
- padding: 1,
166
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
99
167
  margin: 1,
100
168
  borderStyle: "round",
101
169
  borderColor: "yellow",
102
- align: "left",
170
+ align: "center",
171
+ width: 40,
103
172
  })
104
173
  );
105
174
 
@@ -152,20 +221,25 @@ async function performUpdate(packageName: string): Promise<void> {
152
221
  });
153
222
 
154
223
  spinner.succeed(colors.green("更新成功!"));
224
+
225
+ // 清理缓存文件
226
+ clearUpdateCache();
227
+
155
228
  console.log("");
156
229
  console.log(
157
230
  boxen(
158
231
  [
159
- colors.bold("✨ 更新完成!"),
232
+ colors.green(colors.bold("✨ 更新完成!")),
160
233
  "",
161
234
  colors.dim("请重新打开终端使用新版本"),
162
235
  ].join("\n"),
163
236
  {
164
- padding: 1,
237
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
165
238
  margin: { top: 0, bottom: 1, left: 2, right: 2 },
166
239
  borderStyle: "round",
167
240
  borderColor: "green",
168
- align: "left",
241
+ align: "center",
242
+ width: 40,
169
243
  }
170
244
  )
171
245
  );
@@ -181,6 +255,20 @@ async function performUpdate(packageName: string): Promise<void> {
181
255
  }
182
256
  }
183
257
 
258
+ /**
259
+ * 清理更新缓存文件
260
+ */
261
+ export function clearUpdateCache(): void {
262
+ try {
263
+ const cacheFile = join(homedir(), CACHE_FILE);
264
+ if (existsSync(cacheFile)) {
265
+ unlinkSync(cacheFile);
266
+ }
267
+ } catch {
268
+ // 静默失败
269
+ }
270
+ }
271
+
184
272
  /**
185
273
  * 读取缓存
186
274
  */