@zjex/git-workflow 0.2.5 → 0.2.6

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 读取版本号
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();