@zjex/git-workflow 0.2.5 → 0.2.7

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.
@@ -1,5 +1,5 @@
1
1
  import { existsSync, writeFileSync } from "fs";
2
- import { select, input, confirm } from "@inquirer/prompts";
2
+ import { select, input } from "@inquirer/prompts";
3
3
  import { colors, theme, divider } from "../utils.js";
4
4
  import type { GwConfig } from "../config.js";
5
5
 
@@ -22,9 +22,12 @@ const DEFAULT_COMMIT_EMOJIS = {
22
22
 
23
23
  export async function init(): Promise<void> {
24
24
  if (existsSync(CONFIG_FILE)) {
25
- const overwrite = await confirm({
25
+ const overwrite = await select({
26
26
  message: `${CONFIG_FILE} 已存在,是否覆盖?`,
27
- default: false,
27
+ choices: [
28
+ { name: "否,取消", value: false },
29
+ { name: "是,覆盖", value: true },
30
+ ],
28
31
  theme,
29
32
  });
30
33
  if (!overwrite) {
@@ -64,9 +67,12 @@ export async function init(): Promise<void> {
64
67
  divider();
65
68
 
66
69
  // ID 配置
67
- const requireId = await confirm({
70
+ const requireId = await select({
68
71
  message: "是否要求必填 ID (Story ID / Issue ID)?",
69
- default: false,
72
+ choices: [
73
+ { name: "否", value: false },
74
+ { name: "是", value: true },
75
+ ],
70
76
  theme,
71
77
  });
72
78
  if (requireId) config.requireId = true;
@@ -110,16 +116,22 @@ export async function init(): Promise<void> {
110
116
  divider();
111
117
 
112
118
  // Commit 配置
113
- const autoStage = await confirm({
119
+ const autoStage = await select({
114
120
  message: "Commit 时是否自动暂存所有更改?",
115
- default: true,
121
+ choices: [
122
+ { name: "是", value: true },
123
+ { name: "否", value: false },
124
+ ],
116
125
  theme,
117
126
  });
118
127
  if (!autoStage) config.autoStage = false;
119
128
 
120
- const useEmoji = await confirm({
129
+ const useEmoji = await select({
121
130
  message: "Commit 时是否使用 emoji?",
122
- default: true,
131
+ choices: [
132
+ { name: "是", value: true },
133
+ { name: "否", value: false },
134
+ ],
123
135
  theme,
124
136
  });
125
137
  if (!useEmoji) config.useEmoji = false;
@@ -129,6 +141,122 @@ export async function init(): Promise<void> {
129
141
 
130
142
  divider();
131
143
 
144
+ // AI Commit 配置
145
+ console.log(
146
+ colors.dim("\nAI Commit 配置 (使用 AI 自动生成 commit message)\n")
147
+ );
148
+
149
+ const enableAI = await select({
150
+ message: "是否启用 AI Commit 功能?",
151
+ choices: [
152
+ { name: "是(推荐)", value: true },
153
+ { name: "否", value: false },
154
+ ],
155
+ theme,
156
+ });
157
+
158
+ if (enableAI) {
159
+ const aiProvider = await select({
160
+ message: "选择 AI 提供商:",
161
+ choices: [
162
+ {
163
+ name: "GitHub Models(免费,推荐)",
164
+ value: "github",
165
+ description: "使用 GitHub 账号,每天 150 次免费",
166
+ },
167
+ {
168
+ name: "Groq(免费)",
169
+ value: "groq",
170
+ description: "需要注册,每天 14,400 次免费",
171
+ },
172
+ {
173
+ name: "OpenAI(付费)",
174
+ value: "openai",
175
+ description: "需要付费 API key",
176
+ },
177
+ {
178
+ name: "Claude(付费)",
179
+ value: "claude",
180
+ description: "需要付费 API key",
181
+ },
182
+ {
183
+ name: "Ollama(本地)",
184
+ value: "ollama",
185
+ description: "需要安装 Ollama",
186
+ },
187
+ ],
188
+ theme,
189
+ });
190
+
191
+ const useBuiltinKey = await select({
192
+ message: "API Key 配置:",
193
+ choices: [
194
+ {
195
+ name: "使用内置 Key(开箱即用)",
196
+ value: true,
197
+ description: "使用工具内置的 API key,共享限额",
198
+ },
199
+ {
200
+ name: "使用自己的 Key(推荐)",
201
+ value: false,
202
+ description: "配置自己的 API key,独享限额",
203
+ },
204
+ ],
205
+ theme,
206
+ });
207
+
208
+ let apiKey = "";
209
+ if (!useBuiltinKey) {
210
+ apiKey = await input({
211
+ message: `输入你的 ${
212
+ aiProvider === "github" ? "GitHub Token" : "API Key"
213
+ }:`,
214
+ validate: (value) => {
215
+ if (!value.trim()) return "API Key 不能为空";
216
+ return true;
217
+ },
218
+ theme,
219
+ });
220
+ }
221
+
222
+ const language = await select({
223
+ message: "生成的 commit message 语言:",
224
+ choices: [
225
+ { name: "中文", value: "zh-CN" },
226
+ { name: "English", value: "en-US" },
227
+ ],
228
+ theme,
229
+ });
230
+
231
+ config.aiCommit = {
232
+ enabled: true,
233
+ provider: aiProvider as
234
+ | "github"
235
+ | "groq"
236
+ | "openai"
237
+ | "claude"
238
+ | "ollama",
239
+ apiKey: apiKey || undefined,
240
+ language: language as "zh-CN" | "en-US",
241
+ };
242
+
243
+ // 根据提供商设置默认模型
244
+ const defaultModels: Record<string, string> = {
245
+ github: "gpt-4o-mini",
246
+ groq: "llama-3.1-8b-instant",
247
+ openai: "gpt-4o-mini",
248
+ claude: "claude-3-haiku-20240307",
249
+ ollama: "qwen2.5-coder:7b",
250
+ };
251
+ config.aiCommit.model = defaultModels[aiProvider];
252
+ } else {
253
+ config.aiCommit = {
254
+ enabled: false,
255
+ };
256
+ }
257
+
258
+ divider();
259
+
132
260
  // 写入配置
133
261
  const content = JSON.stringify(config, null, 2);
134
262
  writeFileSync(CONFIG_FILE, content + "\n");
@@ -139,5 +267,24 @@ export async function init(): Promise<void> {
139
267
  "\n提示: 可以在配置文件中修改 commitEmojis 来自定义各类型的 emoji"
140
268
  )
141
269
  );
270
+
271
+ if (config.aiCommit?.enabled) {
272
+ console.log(
273
+ colors.dim(
274
+ "提示: AI Commit 已启用,运行 'gw c' 时可以选择 AI 自动生成 commit message"
275
+ )
276
+ );
277
+ if (!config.aiCommit.apiKey) {
278
+ console.log(
279
+ colors.yellow(
280
+ "\n⚠️ 当前使用内置 API key,建议配置自己的 key 以获得更好的体验"
281
+ )
282
+ );
283
+ console.log(
284
+ colors.dim(" 获取方法: https://github.com/settings/tokens/new")
285
+ );
286
+ }
287
+ }
288
+
142
289
  console.log(colors.dim("\n" + content));
143
290
  }
package/src/config.ts CHANGED
@@ -36,6 +36,15 @@ export interface GwConfig {
36
36
  chore?: string;
37
37
  revert?: string;
38
38
  };
39
+ // AI commit 配置
40
+ aiCommit?: {
41
+ enabled?: boolean; // 是否启用 AI commit,默认 true
42
+ provider?: "github" | "groq" | "openai" | "claude" | "ollama"; // AI 提供商,默认 github
43
+ apiKey?: string; // API key,空则使用内置 key
44
+ model?: string; // 模型名称
45
+ language?: "zh-CN" | "en-US"; // 生成语言,默认 zh-CN
46
+ maxTokens?: number; // 最大 token 数,默认 200
47
+ };
39
48
  }
40
49
 
41
50
  const defaultConfig: GwConfig = {
package/src/index.ts CHANGED
@@ -12,17 +12,39 @@ 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 { checkForUpdates } from "./update-notifier.js";
16
15
 
17
16
  // 捕获 Ctrl+C 退出,静默处理
18
17
  process.on("uncaughtException", (err) => {
19
18
  if (err instanceof ExitPromptError) {
19
+ console.log(""); // 输出空行,让界面更整洁
20
20
  process.exit(0);
21
21
  }
22
22
  console.error(err);
23
23
  process.exit(1);
24
24
  });
25
25
 
26
+ // 捕获未处理的 Promise 拒绝
27
+ process.on("unhandledRejection", (reason) => {
28
+ if (reason instanceof ExitPromptError) {
29
+ console.log("");
30
+ process.exit(0);
31
+ }
32
+ console.error("未处理的 Promise 拒绝:", reason);
33
+ process.exit(1);
34
+ });
35
+
36
+ // 捕获 SIGINT 信号 (Ctrl+C)
37
+ process.on("SIGINT", () => {
38
+ console.log("");
39
+ process.exit(0);
40
+ });
41
+
42
+ // 捕获 SIGTERM 信号
43
+ process.on("SIGTERM", () => {
44
+ console.log("");
45
+ process.exit(0);
46
+ });
47
+
26
48
  declare const __VERSION__: string | undefined;
27
49
 
28
50
  // 开发环境下从 package.json 读取版本号
@@ -145,18 +145,18 @@ async function performUpdate(packageName: string): Promise<void> {
145
145
  }).start();
146
146
 
147
147
  try {
148
- // 先尝试卸载旧版本(无 scope 的版本)
148
+ // 先卸载当前版本,确保干净安装
149
149
  try {
150
- execSync("npm uninstall -g git-workflow", {
150
+ execSync(`npm uninstall -g ${packageName}`, {
151
151
  encoding: "utf-8",
152
152
  stdio: ["pipe", "pipe", "pipe"],
153
153
  });
154
- spinner.text = "已卸载旧版本,正在安装新版本...";
154
+ spinner.text = "正在安装新版本...";
155
155
  } catch {
156
- // 旧版本不存在,忽略错误
156
+ // 当前版本不存在,忽略错误
157
157
  }
158
158
 
159
- // 执行更新命令
159
+ // 执行安装命令
160
160
  execSync(`npm install -g ${packageName}`, {
161
161
  encoding: "utf-8",
162
162
  stdio: ["pipe", "pipe", "pipe"],
@@ -164,8 +164,26 @@ async function performUpdate(packageName: string): Promise<void> {
164
164
 
165
165
  spinner.succeed(colors.green("更新成功!"));
166
166
  console.log("");
167
- console.log(colors.cyan(" 提示: 请重新运行命令以使用新版本"));
168
- console.log("");
167
+ console.log(
168
+ boxen(
169
+ [
170
+ colors.bold("✨ 更新完成!"),
171
+ "",
172
+ colors.dim("请运行以下命令刷新并使用新版本:"),
173
+ "",
174
+ colors.yellow(" hash -r && gw --version"),
175
+ "",
176
+ colors.dim("或者重新打开终端"),
177
+ ].join("\n"),
178
+ {
179
+ padding: 1,
180
+ margin: { top: 0, bottom: 1, left: 2, right: 2 },
181
+ borderStyle: "round",
182
+ borderColor: "green",
183
+ align: "left",
184
+ }
185
+ )
186
+ );
169
187
 
170
188
  // 更新成功后退出,让用户重新运行
171
189
  process.exit(0);
@@ -173,10 +191,6 @@ async function performUpdate(packageName: string): Promise<void> {
173
191
  spinner.fail(colors.red("更新失败"));
174
192
  console.log("");
175
193
  console.log(colors.dim(" 你可以手动运行以下命令更新:"));
176
- console.log(colors.yellow(" # 如果之前安装过旧版本,先卸载:"));
177
- console.log(colors.cyan(" npm uninstall -g git-workflow"));
178
- console.log("");
179
- console.log(colors.yellow(" # 然后安装新版本:"));
180
194
  console.log(colors.cyan(` npm install -g ${packageName}`));
181
195
  console.log("");
182
196
  }
package/test-ctrl-c.mjs DELETED
@@ -1,32 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { select } from "@inquirer/prompts";
4
- import { ExitPromptError } from "@inquirer/core";
5
-
6
- // 捕获 Ctrl+C 退出,静默处理
7
- process.on("uncaughtException", (err) => {
8
- if (err instanceof ExitPromptError) {
9
- console.log("\n✓ Ctrl+C 被正确捕获,程序退出");
10
- process.exit(0);
11
- }
12
- console.error("其他错误:", err);
13
- process.exit(1);
14
- });
15
-
16
- async function test() {
17
- try {
18
- const choice = await select({
19
- message: "测试 Ctrl+C (按 Ctrl+C 退出):",
20
- choices: [
21
- { name: "选项 1", value: "1" },
22
- { name: "选项 2", value: "2" },
23
- ],
24
- });
25
- console.log(`你选择了: ${choice}`);
26
- } catch (error) {
27
- console.log("\n在 catch 中捕获到错误,重新抛出...");
28
- throw error;
29
- }
30
- }
31
-
32
- test();
@@ -1,108 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import boxen from "boxen";
4
- import { select } from "@inquirer/prompts";
5
- import { ExitPromptError } from "@inquirer/core";
6
-
7
- // 捕获 Ctrl+C 退出,静默处理
8
- process.on("uncaughtException", (err) => {
9
- if (err instanceof ExitPromptError) {
10
- process.exit(0);
11
- }
12
- console.error(err);
13
- process.exit(1);
14
- });
15
-
16
- const colors = {
17
- green: (s) => `\x1b[32m${s}\x1b[0m`,
18
- dim: (s) => `\x1b[2m${s}\x1b[0m`,
19
- bold: (s) => `\x1b[1m${s}\x1b[0m`,
20
- };
21
-
22
- async function checkForUpdates() {
23
- const current = "0.2.3";
24
- const latest = "0.2.4";
25
- const packageName = "@zjex/git-workflow";
26
-
27
- const message = [
28
- colors.bold("🎉 发现新版本可用!"),
29
- "",
30
- `${colors.dim(current)} → ${colors.green(colors.bold(latest))}`,
31
- ].join("\n");
32
-
33
- console.log("");
34
- console.log(
35
- boxen(message, {
36
- padding: 1,
37
- margin: 1,
38
- borderStyle: "round",
39
- borderColor: "yellow",
40
- align: "left",
41
- })
42
- );
43
-
44
- try {
45
- const action = await select({
46
- message: "你想做什么?",
47
- choices: [
48
- {
49
- name: "🚀 立即更新",
50
- value: "update",
51
- description: `运行 npm install -g ${packageName}`,
52
- },
53
- {
54
- name: "⏭️ 稍后更新,继续使用",
55
- value: "continue",
56
- description: "下次启动时会再次提示",
57
- },
58
- {
59
- name: "🙈 跳过此版本 (24h 内不再提示)",
60
- value: "dismiss",
61
- description: "24 小时内不会再提示此版本",
62
- },
63
- ],
64
- });
65
-
66
- if (action === "update") {
67
- console.log("\n模拟更新中...\n");
68
- process.exit(0);
69
- } else if (action === "dismiss") {
70
- console.log(colors.dim("\n已跳过此版本,24 小时内不再提示\n"));
71
- }
72
- } catch (error) {
73
- // 用户按了 Ctrl+C,重新抛出让全局处理
74
- console.log("");
75
- throw error;
76
- }
77
- }
78
-
79
- async function mainMenu() {
80
- // 先检查更新,等待完成
81
- await checkForUpdates();
82
-
83
- // 然后显示主菜单
84
- console.log(
85
- colors.green(`
86
- ███████╗ ██╗███████╗██╗ ██╗
87
- ╚══███╔╝ ██║██╔════╝╚██╗██╔╝
88
- ███╔╝ ██║█████╗ ╚███╔╝
89
- ███╔╝ ██ ██║██╔══╝ ██╔██╗
90
- ███████╗╚█████╔╝███████╗██╔╝ ██╗
91
- ╚══════╝ ╚════╝ ╚══════╝╚═╝ ╚═╝
92
- `)
93
- );
94
- console.log(colors.dim(` git-workflow v0.2.4\n`));
95
-
96
- const choice = await select({
97
- message: "选择操作:",
98
- choices: [
99
- { name: "[1] ✨ 创建 feature 分支 gw f", value: "1" },
100
- { name: "[2] 🐛 创建 hotfix 分支 gw h", value: "2" },
101
- { name: "[3] 🗑️ 删除分支 gw d", value: "3" },
102
- ],
103
- });
104
-
105
- console.log(`\n你选择了: ${choice}\n`);
106
- }
107
-
108
- mainMenu();
@@ -1,98 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import boxen from "boxen";
4
- import { select } from "@inquirer/prompts";
5
- import ora from "ora";
6
-
7
- const colors = {
8
- red: (s) => `\x1b[31m${s}\x1b[0m`,
9
- green: (s) => `\x1b[32m${s}\x1b[0m`,
10
- yellow: (s) => `\x1b[33m${s}\x1b[0m`,
11
- cyan: (s) => `\x1b[36m${s}\x1b[0m`,
12
- dim: (s) => `\x1b[2m${s}\x1b[0m`,
13
- bold: (s) => `\x1b[1m${s}\x1b[0m`,
14
- reset: "\x1b[0m",
15
- };
16
-
17
- async function testUpdateFlow() {
18
- const current = "0.1.0";
19
- const latest = "0.2.0";
20
- const packageName = "@zjex/git-workflow";
21
-
22
- // 1. 显示更新提示框
23
- const message = [
24
- colors.bold("🎉 发现新版本可用!"),
25
- "",
26
- `${colors.dim(current)} → ${colors.green(colors.bold(latest))}`,
27
- ].join("\n");
28
-
29
- console.log("");
30
- console.log(
31
- boxen(message, {
32
- padding: 1,
33
- margin: 1,
34
- borderStyle: "round",
35
- borderColor: "yellow",
36
- align: "left",
37
- })
38
- );
39
-
40
- // 2. 交互式选择
41
- try {
42
- const action = await select({
43
- message: "你想做什么?",
44
- choices: [
45
- {
46
- name: "🚀 立即更新",
47
- value: "update",
48
- description: `运行 npm install -g ${packageName}`,
49
- },
50
- {
51
- name: "⏭️ 稍后更新,继续使用",
52
- value: "continue",
53
- description: "下次启动时会再次提示",
54
- },
55
- {
56
- name: "🙈 跳过此版本 (24h 内不再提示)",
57
- value: "dismiss",
58
- description: "24 小时内不会再提示此版本",
59
- },
60
- ],
61
- });
62
-
63
- console.log("");
64
-
65
- // 3. 根据选择执行操作
66
- if (action === "update") {
67
- // 模拟更新过程
68
- const spinner = ora({
69
- text: "正在更新...",
70
- spinner: "dots",
71
- }).start();
72
-
73
- // 模拟卸载旧版本
74
- await new Promise((resolve) => setTimeout(resolve, 1000));
75
- spinner.text = "已卸载旧版本,正在安装新版本...";
76
-
77
- // 模拟安装新版本
78
- await new Promise((resolve) => setTimeout(resolve, 2000));
79
-
80
- spinner.succeed(colors.green("更新成功!"));
81
- console.log("");
82
- console.log(colors.cyan(" 提示: 请重新运行命令以使用新版本"));
83
- console.log("");
84
- } else if (action === "continue") {
85
- console.log(colors.cyan("继续使用当前版本..."));
86
- console.log("");
87
- } else if (action === "dismiss") {
88
- console.log(colors.dim("已跳过此版本,24 小时内不再提示"));
89
- console.log("");
90
- }
91
- } catch (error) {
92
- console.log("");
93
- console.log(colors.dim("已取消"));
94
- console.log("");
95
- }
96
- }
97
-
98
- testUpdateFlow();