@zjex/git-workflow 0.3.8 → 0.3.10

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
@@ -12,7 +12,7 @@
12
12
  <a href="https://github.com/iamzjt-front-end/git-workflow"><img src="https://img.shields.io/github/stars/iamzjt-front-end/git-workflow?style=flat&colorA=18181B&colorB=F59E0B" alt="github stars"></a>
13
13
  <a href="https://github.com/iamzjt-front-end/git-workflow/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@zjex/git-workflow?style=flat&colorA=18181B&colorB=10B981" alt="license"></a>
14
14
  <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-339933?style=flat&logo=node.js&logoColor=white&colorA=18181B" alt="node version"></a>
15
- <a href="https://github.com/iamzjt-front-end/git-workflow/actions"><img src="https://img.shields.io/badge/tests-267%20passed-success?style=flat&colorA=18181B" alt="tests"></a>
15
+ <a href="https://github.com/iamzjt-front-end/git-workflow/actions"><img src="https://img.shields.io/badge/tests-290%20passed-success?style=flat&colorA=18181B" alt="tests"></a>
16
16
  <a href="https://github.com/iamzjt-front-end/git-workflow/issues"><img src="https://img.shields.io/github/issues/iamzjt-front-end/git-workflow?style=flat&colorA=18181B&colorB=EC4899" alt="issues"></a>
17
17
  </p>
18
18
 
package/dist/index.js CHANGED
@@ -179,11 +179,10 @@ function showSimpleNotification(current, latest) {
179
179
  )} \u2192 ${colors.green(latest)} ${colors.dim("\u8FD0\u884C")} ${colors.cyan(
180
180
  "gw update"
181
181
  )} ${colors.dim("\u66F4\u65B0")}`;
182
- console.log("");
183
182
  console.log(
184
183
  boxen(message, {
185
184
  padding: { top: 0, bottom: 0, left: 2, right: 2 },
186
- margin: { top: 0, bottom: 1, left: 0, right: 0 },
185
+ margin: { top: 0, bottom: 0, left: 0, right: 0 },
187
186
  borderStyle: "round",
188
187
  borderColor: "yellow",
189
188
  align: "center"
@@ -431,12 +430,22 @@ async function getBranchName(type) {
431
430
  console.log(colors.red(`${idLabel}\u4E0D\u80FD\u4E3A\u7A7A`));
432
431
  return null;
433
432
  }
434
- const description = await input({ message: "\u8BF7\u8F93\u5165\u63CF\u8FF0:", theme });
435
- if (!description) {
433
+ const requireDescription = type === "feature" ? config2.featureRequireDescription ?? false : config2.hotfixRequireDescription ?? false;
434
+ const descMessage = requireDescription ? "\u8BF7\u8F93\u5165\u63CF\u8FF0:" : "\u8BF7\u8F93\u5165\u63CF\u8FF0 (\u53EF\u8DF3\u8FC7):";
435
+ const description = await input({ message: descMessage, theme });
436
+ if (requireDescription && !description) {
436
437
  console.log(colors.red("\u63CF\u8FF0\u4E0D\u80FD\u4E3A\u7A7A"));
437
438
  return null;
438
439
  }
439
- return id ? `${branchPrefix}/${TODAY}-${id}-${description}` : `${branchPrefix}/${TODAY}-${description}`;
440
+ if (id && description) {
441
+ return `${branchPrefix}/${TODAY}-${id}-${description}`;
442
+ } else if (id) {
443
+ return `${branchPrefix}/${TODAY}-${id}`;
444
+ } else if (description) {
445
+ return `${branchPrefix}/${TODAY}-${description}`;
446
+ } else {
447
+ return `${branchPrefix}/${TODAY}`;
448
+ }
440
449
  }
441
450
  async function createBranch(type, baseBranchArg) {
442
451
  const config2 = getConfig();
@@ -1327,6 +1336,25 @@ async function init() {
1327
1336
  });
1328
1337
  config2.hotfixIdLabel = hotfixIdLabel;
1329
1338
  divider();
1339
+ const featureRequireDescription = await select4({
1340
+ message: "Feature \u5206\u652F\u662F\u5426\u8981\u6C42\u5FC5\u586B\u63CF\u8FF0?",
1341
+ choices: [
1342
+ { name: "\u5426", value: false },
1343
+ { name: "\u662F", value: true }
1344
+ ],
1345
+ theme
1346
+ });
1347
+ config2.featureRequireDescription = featureRequireDescription;
1348
+ const hotfixRequireDescription = await select4({
1349
+ message: "Hotfix \u5206\u652F\u662F\u5426\u8981\u6C42\u5FC5\u586B\u63CF\u8FF0?",
1350
+ choices: [
1351
+ { name: "\u5426", value: false },
1352
+ { name: "\u662F", value: true }
1353
+ ],
1354
+ theme
1355
+ });
1356
+ config2.hotfixRequireDescription = hotfixRequireDescription;
1357
+ divider();
1330
1358
  const defaultTagPrefix = await input3({
1331
1359
  message: "\u9ED8\u8BA4 Tag \u524D\u7F00 (\u7559\u7A7A\u5219\u6BCF\u6B21\u9009\u62E9):",
1332
1360
  theme
@@ -2104,7 +2132,12 @@ async function generateAICommitMessage(config2) {
2104
2132
  }
2105
2133
  const maxDiffLength = detailedDescription ? 6e3 : 4e3;
2106
2134
  const truncatedDiff = diff.length > maxDiffLength ? diff.slice(0, maxDiffLength) + "\n..." : diff;
2107
- const prompt = buildPrompt(truncatedDiff, language, detailedDescription, useEmoji);
2135
+ const prompt = buildPrompt(
2136
+ truncatedDiff,
2137
+ language,
2138
+ detailedDescription,
2139
+ useEmoji
2140
+ );
2108
2141
  const providerInfo = AI_PROVIDERS[provider];
2109
2142
  if (!providerInfo) {
2110
2143
  throw new Error(`\u4E0D\u652F\u6301\u7684 AI \u63D0\u4F9B\u5546: ${provider}`);
@@ -2916,7 +2949,7 @@ process.on("SIGTERM", () => {
2916
2949
  console.log("");
2917
2950
  process.exit(0);
2918
2951
  });
2919
- var version = true ? "0.3.8" : "0.0.0-dev";
2952
+ var version = true ? "0.3.10" : "0.0.0-dev";
2920
2953
  async function mainMenu() {
2921
2954
  console.log(
2922
2955
  colors.green(`
@@ -3109,11 +3142,58 @@ cli.command("log", "\u4EA4\u4E92\u5F0FGit\u65E5\u5FD7\u67E5\u770B (\u5206\u9875\
3109
3142
  if (options.limit) logOptions.limit = parseInt(options.limit);
3110
3143
  return log(logOptions);
3111
3144
  });
3112
- cli.command("clean", "\u6E05\u7406\u7F13\u5B58\u6587\u4EF6").action(async () => {
3145
+ cli.command("clean", "\u6E05\u7406\u7F13\u5B58\u548C\u4E34\u65F6\u6587\u4EF6").action(async () => {
3113
3146
  const { clearUpdateCache: clearUpdateCache3 } = await Promise.resolve().then(() => (init_update_notifier(), update_notifier_exports));
3147
+ const { existsSync: existsSync5, unlinkSync: unlinkSync4, readdirSync } = await import("fs");
3148
+ const { homedir: homedir5, tmpdir: tmpdir2 } = await import("os");
3149
+ const { join: join6 } = await import("path");
3150
+ const { select: select9 } = await import("@inquirer/prompts");
3151
+ let cleanedCount = 0;
3152
+ let deletedGlobalConfig = false;
3153
+ const globalConfig = join6(homedir5(), ".gwrc.json");
3154
+ const hasGlobalConfig = existsSync5(globalConfig);
3155
+ if (hasGlobalConfig) {
3156
+ const shouldDeleteConfig = await select9({
3157
+ message: "\u68C0\u6D4B\u5230\u5168\u5C40\u914D\u7F6E\u6587\u4EF6\uFF0C\u662F\u5426\u5220\u9664\uFF1F",
3158
+ choices: [
3159
+ { name: "\u5426\uFF0C\u4FDD\u7559\u914D\u7F6E\u6587\u4EF6", value: false },
3160
+ { name: "\u662F\uFF0C\u5220\u9664\u914D\u7F6E\u6587\u4EF6", value: true }
3161
+ ],
3162
+ theme
3163
+ });
3164
+ if (shouldDeleteConfig) {
3165
+ try {
3166
+ unlinkSync4(globalConfig);
3167
+ cleanedCount++;
3168
+ deletedGlobalConfig = true;
3169
+ } catch {
3170
+ }
3171
+ }
3172
+ }
3114
3173
  clearUpdateCache3();
3174
+ cleanedCount++;
3175
+ try {
3176
+ const tmpDir = tmpdir2();
3177
+ const files = readdirSync(tmpDir);
3178
+ const gwTmpFiles = files.filter((f) => f.startsWith(".gw-commit-msg-"));
3179
+ for (const file of gwTmpFiles) {
3180
+ try {
3181
+ unlinkSync4(join6(tmpDir, file));
3182
+ cleanedCount++;
3183
+ } catch {
3184
+ }
3185
+ }
3186
+ } catch {
3187
+ }
3115
3188
  console.log("");
3116
- console.log(colors.green("\u2714 \u7F13\u5B58\u5DF2\u6E05\u7406"));
3189
+ console.log(colors.green(`\u2714 \u5DF2\u6E05\u7406 ${cleanedCount} \u4E2A\u6587\u4EF6`));
3190
+ if (deletedGlobalConfig) {
3191
+ console.log("");
3192
+ console.log(colors.yellow("\u26A0\uFE0F \u5168\u5C40\u914D\u7F6E\u6587\u4EF6\u5DF2\u5220\u9664"));
3193
+ console.log(
3194
+ colors.dim(` \u5982\u9700\u91CD\u65B0\u914D\u7F6E\uFF0C\u8BF7\u8FD0\u884C: ${colors.cyan("gw init")}`)
3195
+ );
3196
+ }
3117
3197
  console.log("");
3118
3198
  });
3119
3199
  cli.option("-v, --version", "\u663E\u793A\u7248\u672C\u53F7");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zjex/git-workflow",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "🚀 极简的 Git 工作流 CLI 工具,让分支管理和版本发布变得轻松愉快",
5
5
  "type": "module",
6
6
  "bin": {
package/src/ai-service.ts CHANGED
@@ -60,7 +60,12 @@ function getGitDiff(): string {
60
60
  /**
61
61
  * 构建 AI prompt
62
62
  */
63
- function buildPrompt(diff: string, language: string, detailedDescription: boolean = false, useEmoji: boolean = true): string {
63
+ function buildPrompt(
64
+ diff: string,
65
+ language: string,
66
+ detailedDescription: boolean = false,
67
+ useEmoji: boolean = true
68
+ ): string {
64
69
  const isZh = language === "zh-CN";
65
70
 
66
71
  if (detailedDescription) {
@@ -69,7 +74,7 @@ function buildPrompt(diff: string, language: string, detailedDescription: boolea
69
74
  ? `你是一个专业的 Git commit message 生成助手。请根据提供的 git diff 生成符合 Conventional Commits 规范的详细 commit message。
70
75
 
71
76
  格式要求:
72
- 1. 第一行:${useEmoji ? '<emoji> ' : ''}<type>(<scope>): <subject>
77
+ 1. 第一行:${useEmoji ? "<emoji> " : ""}<type>(<scope>): <subject>
73
78
  2. 空行
74
79
  3. 详细描述:列出主要修改点,每个修改点一行,以 "- " 开头
75
80
 
@@ -80,7 +85,9 @@ function buildPrompt(diff: string, language: string, detailedDescription: boolea
80
85
  - 详细描述要列出 3-6 个主要修改点,每个修改点简洁明了
81
86
  - 如果修改较少,可以只列出 2-3 个修改点
82
87
  - 不要有其他解释或多余内容
83
- ${useEmoji ? `
88
+ ${
89
+ useEmoji
90
+ ? `
84
91
  Emoji 映射规则:
85
92
  - feat: ✨ (新功能)
86
93
  - fix: 🐛 (修复Bug)
@@ -92,10 +99,12 @@ Emoji 映射规则:
92
99
  - build: 📦 (构建)
93
100
  - ci: 👷 (CI/CD)
94
101
  - chore: 🔧 (其他杂项)
95
- - revert: ⏪ (回滚)` : ''}
102
+ - revert: ⏪ (回滚)`
103
+ : ""
104
+ }
96
105
 
97
106
  示例:
98
- ${useEmoji ? '' : ''}feat(auth): 添加用户登录功能
107
+ ${useEmoji ? "" : ""}feat(auth): 添加用户登录功能
99
108
 
100
109
  - 实现用户名密码登录接口
101
110
  - 添加登录状态验证中间件
@@ -104,7 +113,7 @@ ${useEmoji ? '✨ ' : ''}feat(auth): 添加用户登录功能
104
113
  : `You are a professional Git commit message generator. Generate a detailed commit message following Conventional Commits specification based on the provided git diff.
105
114
 
106
115
  Format requirements:
107
- 1. First line: ${useEmoji ? '<emoji> ' : ''}<type>(<scope>): <subject>
116
+ 1. First line: ${useEmoji ? "<emoji> " : ""}<type>(<scope>): <subject>
108
117
  2. Empty line
109
118
  3. Detailed description: List main changes, one per line, starting with "- "
110
119
 
@@ -115,7 +124,9 @@ Rules:
115
124
  - Detailed description should list 3-6 main changes, each change should be concise
116
125
  - If changes are minimal, list 2-3 changes
117
126
  - No explanations or extra content
118
- ${useEmoji ? `
127
+ ${
128
+ useEmoji
129
+ ? `
119
130
  Emoji mapping rules:
120
131
  - feat: ✨ (new feature)
121
132
  - fix: 🐛 (bug fix)
@@ -127,10 +138,12 @@ Emoji mapping rules:
127
138
  - build: 📦 (build)
128
139
  - ci: 👷 (CI/CD)
129
140
  - chore: 🔧 (chore)
130
- - revert: ⏪ (revert)` : ''}
141
+ - revert: ⏪ (revert)`
142
+ : ""
143
+ }
131
144
 
132
145
  Example:
133
- ${useEmoji ? '' : ''}feat(auth): add user login functionality
146
+ ${useEmoji ? "" : ""}feat(auth): add user login functionality
134
147
 
135
148
  - Implement username/password login API
136
149
  - Add login status validation middleware
@@ -148,13 +161,15 @@ ${useEmoji ? '✨ ' : ''}feat(auth): add user login functionality
148
161
  ? `你是一个专业的 Git commit message 生成助手。请根据提供的 git diff 生成符合 Conventional Commits 规范的 commit message。
149
162
 
150
163
  规则:
151
- 1. 格式:${useEmoji ? '<emoji> ' : ''}<type>(<scope>): <subject>
164
+ 1. 格式:${useEmoji ? "<emoji> " : ""}<type>(<scope>): <subject>
152
165
  2. type 必须是以下之一:feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
153
166
  3. scope 是可选的,表示影响范围
154
167
  4. subject 用中文描述,简洁明了,不超过 50 字
155
168
  5. 只返回一条 commit message,即使有多个文件改动也要总结成一条
156
169
  6. 不要有其他解释或多余内容
157
- ${useEmoji ? `
170
+ ${
171
+ useEmoji
172
+ ? `
158
173
  Emoji 映射规则:
159
174
  - feat: ✨ (新功能)
160
175
  - fix: 🐛 (修复Bug)
@@ -166,22 +181,26 @@ Emoji 映射规则:
166
181
  - build: 📦 (构建)
167
182
  - ci: 👷 (CI/CD)
168
183
  - chore: 🔧 (其他杂项)
169
- - revert: ⏪ (回滚)` : ''}
184
+ - revert: ⏪ (回滚)`
185
+ : ""
186
+ }
170
187
 
171
188
  示例:
172
- - ${useEmoji ? '' : ''}feat(auth): 添加用户登录功能
173
- - ${useEmoji ? '🐛 ' : ''}fix(api): 修复数据获取失败的问题
174
- - ${useEmoji ? '📝 ' : ''}docs(readme): 更新安装说明`
189
+ - ${useEmoji ? "" : ""}feat(auth): 添加用户登录功能
190
+ - ${useEmoji ? "🐛 " : ""}fix(api): 修复数据获取失败的问题
191
+ - ${useEmoji ? "📝 " : ""}docs(readme): 更新安装说明`
175
192
  : `You are a professional Git commit message generator. Generate a commit message following Conventional Commits specification based on the provided git diff.
176
193
 
177
194
  Rules:
178
- 1. Format: ${useEmoji ? '<emoji> ' : ''}<type>(<scope>): <subject>
195
+ 1. Format: ${useEmoji ? "<emoji> " : ""}<type>(<scope>): <subject>
179
196
  2. type must be one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
180
197
  3. scope is optional, indicates the affected area
181
198
  4. subject should be concise, no more than 50 characters
182
199
  5. Return only ONE commit message, even if multiple files are changed, summarize into one message
183
200
  6. No explanations or extra content
184
- ${useEmoji ? `
201
+ ${
202
+ useEmoji
203
+ ? `
185
204
  Emoji mapping rules:
186
205
  - feat: ✨ (new feature)
187
206
  - fix: 🐛 (bug fix)
@@ -193,12 +212,14 @@ Emoji mapping rules:
193
212
  - build: 📦 (build)
194
213
  - ci: 👷 (CI/CD)
195
214
  - chore: 🔧 (chore)
196
- - revert: ⏪ (revert)` : ''}
215
+ - revert: ⏪ (revert)`
216
+ : ""
217
+ }
197
218
 
198
219
  Examples:
199
- - ${useEmoji ? '' : ''}feat(auth): add user login functionality
200
- - ${useEmoji ? '🐛 ' : ''}fix(api): resolve data fetching failure
201
- - ${useEmoji ? '📝 ' : ''}docs(readme): update installation guide`;
220
+ - ${useEmoji ? "" : ""}feat(auth): add user login functionality
221
+ - ${useEmoji ? "🐛 " : ""}fix(api): resolve data fetching failure
222
+ - ${useEmoji ? "📝 " : ""}docs(readme): update installation guide`;
202
223
 
203
224
  const userPrompt = isZh
204
225
  ? `请根据以下 git diff 生成 commit message:\n\n${diff}`
@@ -347,25 +368,29 @@ async function callOllamaAPI(
347
368
  */
348
369
  function cleanAIResponse(response: string): string {
349
370
  // 移除开头的特殊字符(如 ...、```、等)
350
- let cleaned = response.replace(/^[.\s`~-]+/, '').trim();
351
-
371
+ let cleaned = response.replace(/^[.\s`~-]+/, "").trim();
372
+
352
373
  // 移除结尾的特殊字符
353
- cleaned = cleaned.replace(/[.\s`~-]+$/, '').trim();
354
-
355
- const lines = cleaned.split('\n').map(line => line.trim()).filter(line => line);
356
-
374
+ cleaned = cleaned.replace(/[.\s`~-]+$/, "").trim();
375
+
376
+ // 分割成行并过滤空行
377
+ const lines = cleaned
378
+ .split("\n")
379
+ .map((line) => line.trim())
380
+ .filter((line) => line);
381
+
357
382
  // 移除重复的行
358
383
  const uniqueLines = [];
359
384
  const seen = new Set();
360
-
385
+
361
386
  for (const line of lines) {
362
387
  if (!seen.has(line)) {
363
388
  seen.add(line);
364
389
  uniqueLines.push(line);
365
390
  }
366
391
  }
367
-
368
- return uniqueLines.join('\n');
392
+
393
+ return uniqueLines.join("\n");
369
394
  }
370
395
 
371
396
  /**
@@ -379,9 +404,12 @@ export async function generateAICommitMessage(
379
404
  const language = aiConfig.language || "zh-CN";
380
405
  const detailedDescription = aiConfig.detailedDescription !== false; // 默认启用详细描述
381
406
  const maxTokens = aiConfig.maxTokens || (detailedDescription ? 400 : 200);
382
-
407
+
383
408
  // AI emoji配置:优先使用aiCommit.useEmoji,如果未设置则使用全局useEmoji,默认true
384
- const useEmoji = aiConfig.useEmoji !== undefined ? aiConfig.useEmoji : (config.useEmoji !== false);
409
+ const useEmoji =
410
+ aiConfig.useEmoji !== undefined
411
+ ? aiConfig.useEmoji
412
+ : config.useEmoji !== false;
385
413
 
386
414
  // 获取 git diff
387
415
  const diff = getGitDiff();
@@ -395,7 +423,12 @@ export async function generateAICommitMessage(
395
423
  diff.length > maxDiffLength ? diff.slice(0, maxDiffLength) + "\n..." : diff;
396
424
 
397
425
  // 构建 prompt
398
- const prompt = buildPrompt(truncatedDiff, language, detailedDescription, useEmoji);
426
+ const prompt = buildPrompt(
427
+ truncatedDiff,
428
+ language,
429
+ detailedDescription,
430
+ useEmoji
431
+ );
399
432
 
400
433
  // 根据提供商调用对应的 API
401
434
  const providerInfo = AI_PROVIDERS[provider];
@@ -32,15 +32,32 @@ export async function getBranchName(type: BranchType): Promise<string | null> {
32
32
  return null;
33
33
  }
34
34
 
35
- const description = await input({ message: "请输入描述:", theme });
36
- if (!description) {
35
+ // 描述是否必填,默认非必填
36
+ const requireDescription =
37
+ type === "feature"
38
+ ? config.featureRequireDescription ?? false
39
+ : config.hotfixRequireDescription ?? false;
40
+ const descMessage = requireDescription
41
+ ? "请输入描述:"
42
+ : "请输入描述 (可跳过):";
43
+
44
+ const description = await input({ message: descMessage, theme });
45
+
46
+ if (requireDescription && !description) {
37
47
  console.log(colors.red("描述不能为空"));
38
48
  return null;
39
49
  }
40
50
 
41
- return id
42
- ? `${branchPrefix}/${TODAY}-${id}-${description}`
43
- : `${branchPrefix}/${TODAY}-${description}`;
51
+ // 构建分支名
52
+ if (id && description) {
53
+ return `${branchPrefix}/${TODAY}-${id}-${description}`;
54
+ } else if (id) {
55
+ return `${branchPrefix}/${TODAY}-${id}`;
56
+ } else if (description) {
57
+ return `${branchPrefix}/${TODAY}-${description}`;
58
+ } else {
59
+ return `${branchPrefix}/${TODAY}`;
60
+ }
44
61
  }
45
62
 
46
63
  export async function createBranch(
@@ -118,6 +118,29 @@ export async function init(): Promise<void> {
118
118
 
119
119
  divider();
120
120
 
121
+ // 描述必填配置
122
+ const featureRequireDescription = await select({
123
+ message: "Feature 分支是否要求必填描述?",
124
+ choices: [
125
+ { name: "否", value: false },
126
+ { name: "是", value: true },
127
+ ],
128
+ theme,
129
+ });
130
+ config.featureRequireDescription = featureRequireDescription;
131
+
132
+ const hotfixRequireDescription = await select({
133
+ message: "Hotfix 分支是否要求必填描述?",
134
+ choices: [
135
+ { name: "否", value: false },
136
+ { name: "是", value: true },
137
+ ],
138
+ theme,
139
+ });
140
+ config.hotfixRequireDescription = hotfixRequireDescription;
141
+
142
+ divider();
143
+
121
144
  // Tag 配置
122
145
  const defaultTagPrefix = await input({
123
146
  message: "默认 Tag 前缀 (留空则每次选择):",
@@ -269,15 +292,16 @@ export async function init(): Promise<void> {
269
292
  const detailedDescription = await select({
270
293
  message: "是否生成详细的修改点描述?",
271
294
  choices: [
272
- {
273
- name: "是(包含修改点列表,推荐)",
295
+ {
296
+ name: "是(包含修改点列表,推荐)",
274
297
  value: true,
275
- description: "如:feat(auth): 添加用户登录功能\n\n- 实现用户名密码登录接口\n- 添加登录状态验证中间件"
298
+ description:
299
+ "如:feat(auth): 添加用户登录功能\n\n- 实现用户名密码登录接口\n- 添加登录状态验证中间件",
276
300
  },
277
- {
278
- name: "否(仅生成标题)",
301
+ {
302
+ name: "否(仅生成标题)",
279
303
  value: false,
280
- description: "如:feat(auth): 添加用户登录功能"
304
+ description: "如:feat(auth): 添加用户登录功能",
281
305
  },
282
306
  ],
283
307
  theme,
@@ -286,20 +310,20 @@ export async function init(): Promise<void> {
286
310
  const aiUseEmoji = await select({
287
311
  message: "AI 生成的 commit message 是否包含 emoji?",
288
312
  choices: [
289
- {
290
- name: "是(推荐)",
313
+ {
314
+ name: "是(推荐)",
291
315
  value: true,
292
- description: "如:✨ feat(auth): 添加用户登录功能"
316
+ description: "如:✨ feat(auth): 添加用户登录功能",
293
317
  },
294
- {
295
- name: "否",
318
+ {
319
+ name: "否",
296
320
  value: false,
297
- description: "如:feat(auth): 添加用户登录功能"
321
+ description: "如:feat(auth): 添加用户登录功能",
298
322
  },
299
- {
300
- name: "跟随全局设置",
323
+ {
324
+ name: "跟随全局设置",
301
325
  value: undefined,
302
- description: `当前全局设置:${useEmoji ? '启用' : '禁用'} emoji`
326
+ description: `当前全局设置:${useEmoji ? "启用" : "禁用"} emoji`,
303
327
  },
304
328
  ],
305
329
  theme,
package/src/config.ts CHANGED
@@ -12,6 +12,10 @@ export interface GwConfig {
12
12
  hotfixPrefix: string;
13
13
  // 是否要求必填 ID,默认 false
14
14
  requireId: boolean;
15
+ // feature 分支是否要求必填描述,默认 false
16
+ featureRequireDescription?: boolean;
17
+ // hotfix 分支是否要求必填描述,默认 false
18
+ hotfixRequireDescription?: boolean;
15
19
  // ID 标签名称
16
20
  featureIdLabel: string;
17
21
  hotfixIdLabel: string;
package/src/index.ts CHANGED
@@ -364,11 +364,75 @@ cli
364
364
  return log(logOptions);
365
365
  });
366
366
 
367
- cli.command("clean", "清理缓存文件").action(async () => {
367
+ cli.command("clean", "清理缓存和临时文件").action(async () => {
368
368
  const { clearUpdateCache } = await import("./update-notifier.js");
369
+ const { existsSync, unlinkSync, readdirSync } = await import("fs");
370
+ const { homedir, tmpdir } = await import("os");
371
+ const { join } = await import("path");
372
+ const { select } = await import("@inquirer/prompts");
373
+
374
+ let cleanedCount = 0;
375
+ let deletedGlobalConfig = false;
376
+
377
+ // 检查全局配置文件是否存在
378
+ const globalConfig = join(homedir(), ".gwrc.json");
379
+ const hasGlobalConfig = existsSync(globalConfig);
380
+
381
+ // 如果有全局配置文件,询问是否删除
382
+ if (hasGlobalConfig) {
383
+ const shouldDeleteConfig = await select({
384
+ message: "检测到全局配置文件,是否删除?",
385
+ choices: [
386
+ { name: "否,保留配置文件", value: false },
387
+ { name: "是,删除配置文件", value: true },
388
+ ],
389
+ theme,
390
+ });
391
+
392
+ if (shouldDeleteConfig) {
393
+ try {
394
+ unlinkSync(globalConfig);
395
+ cleanedCount++;
396
+ deletedGlobalConfig = true;
397
+ } catch {
398
+ // 静默失败
399
+ }
400
+ }
401
+ }
402
+
403
+ // 1. 清理更新缓存
369
404
  clearUpdateCache();
405
+ cleanedCount++;
406
+
407
+ // 2. 清理临时 commit 消息文件
408
+ try {
409
+ const tmpDir = tmpdir();
410
+ const files = readdirSync(tmpDir);
411
+ const gwTmpFiles = files.filter((f) => f.startsWith(".gw-commit-msg-"));
412
+
413
+ for (const file of gwTmpFiles) {
414
+ try {
415
+ unlinkSync(join(tmpDir, file));
416
+ cleanedCount++;
417
+ } catch {
418
+ // 静默失败
419
+ }
420
+ }
421
+ } catch {
422
+ // 静默失败
423
+ }
424
+
370
425
  console.log("");
371
- console.log(colors.green("✔ 缓存已清理"));
426
+ console.log(colors.green(`✔ 已清理 ${cleanedCount} 个文件`));
427
+
428
+ if (deletedGlobalConfig) {
429
+ console.log("");
430
+ console.log(colors.yellow("⚠️ 全局配置文件已删除"));
431
+ console.log(
432
+ colors.dim(` 如需重新配置,请运行: ${colors.cyan("gw init")}`)
433
+ );
434
+ }
435
+
372
436
  console.log("");
373
437
  });
374
438
 
@@ -151,11 +151,10 @@ function showSimpleNotification(current: string, latest: string): void {
151
151
  "gw update"
152
152
  )} ${colors.dim("更新")}`;
153
153
 
154
- console.log("");
155
154
  console.log(
156
155
  boxen(message, {
157
156
  padding: { top: 0, bottom: 0, left: 2, right: 2 },
158
- margin: { top: 0, bottom: 1, left: 0, right: 0 },
157
+ margin: { top: 0, bottom: 0, left: 0, right: 0 },
159
158
  borderStyle: "round",
160
159
  borderColor: "yellow",
161
160
  align: "center",