@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.
package/dist/index.js CHANGED
@@ -274,7 +274,7 @@ async function deleteBranch(branchArg) {
274
274
  }
275
275
  if (branch.startsWith("__remote__")) {
276
276
  const remoteBranch = branch.replace("__remote__", "");
277
- const confirm3 = await select({
277
+ const confirm = await select({
278
278
  message: `\u786E\u8BA4\u5220\u9664\u8FDC\u7A0B\u5206\u652F origin/${remoteBranch}?`,
279
279
  choices: [
280
280
  { name: "\u662F", value: true },
@@ -282,7 +282,7 @@ async function deleteBranch(branchArg) {
282
282
  ],
283
283
  theme
284
284
  });
285
- if (!confirm3) {
285
+ if (!confirm) {
286
286
  console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
287
287
  return;
288
288
  }
@@ -736,7 +736,7 @@ async function release() {
736
736
 
737
737
  // src/commands/init.ts
738
738
  import { existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
739
- import { select as select4, input as input3, confirm } from "@inquirer/prompts";
739
+ import { select as select4, input as input3 } from "@inquirer/prompts";
740
740
  var CONFIG_FILE = ".gwrc.json";
741
741
  var DEFAULT_COMMIT_EMOJIS = {
742
742
  feat: "\u2728",
@@ -753,9 +753,12 @@ var DEFAULT_COMMIT_EMOJIS = {
753
753
  };
754
754
  async function init() {
755
755
  if (existsSync2(CONFIG_FILE)) {
756
- const overwrite = await confirm({
756
+ const overwrite = await select4({
757
757
  message: `${CONFIG_FILE} \u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6?`,
758
- default: false,
758
+ choices: [
759
+ { name: "\u5426\uFF0C\u53D6\u6D88", value: false },
760
+ { name: "\u662F\uFF0C\u8986\u76D6", value: true }
761
+ ],
759
762
  theme
760
763
  });
761
764
  if (!overwrite) {
@@ -784,9 +787,12 @@ async function init() {
784
787
  });
785
788
  if (hotfixPrefix !== "hotfix") config2.hotfixPrefix = hotfixPrefix;
786
789
  divider();
787
- const requireId = await confirm({
790
+ const requireId = await select4({
788
791
  message: "\u662F\u5426\u8981\u6C42\u5FC5\u586B ID (Story ID / Issue ID)?",
789
- default: false,
792
+ choices: [
793
+ { name: "\u5426", value: false },
794
+ { name: "\u662F", value: true }
795
+ ],
790
796
  theme
791
797
  });
792
798
  if (requireId) config2.requireId = true;
@@ -820,20 +826,124 @@ async function init() {
820
826
  if (autoPushChoice === "yes") config2.autoPush = true;
821
827
  if (autoPushChoice === "no") config2.autoPush = false;
822
828
  divider();
823
- const autoStage = await confirm({
829
+ const autoStage = await select4({
824
830
  message: "Commit \u65F6\u662F\u5426\u81EA\u52A8\u6682\u5B58\u6240\u6709\u66F4\u6539?",
825
- default: true,
831
+ choices: [
832
+ { name: "\u662F", value: true },
833
+ { name: "\u5426", value: false }
834
+ ],
826
835
  theme
827
836
  });
828
837
  if (!autoStage) config2.autoStage = false;
829
- const useEmoji = await confirm({
838
+ const useEmoji = await select4({
830
839
  message: "Commit \u65F6\u662F\u5426\u4F7F\u7528 emoji?",
831
- default: true,
840
+ choices: [
841
+ { name: "\u662F", value: true },
842
+ { name: "\u5426", value: false }
843
+ ],
832
844
  theme
833
845
  });
834
846
  if (!useEmoji) config2.useEmoji = false;
835
847
  config2.commitEmojis = DEFAULT_COMMIT_EMOJIS;
836
848
  divider();
849
+ console.log(
850
+ colors.dim("\nAI Commit \u914D\u7F6E (\u4F7F\u7528 AI \u81EA\u52A8\u751F\u6210 commit message)\n")
851
+ );
852
+ const enableAI = await select4({
853
+ message: "\u662F\u5426\u542F\u7528 AI Commit \u529F\u80FD?",
854
+ choices: [
855
+ { name: "\u662F\uFF08\u63A8\u8350\uFF09", value: true },
856
+ { name: "\u5426", value: false }
857
+ ],
858
+ theme
859
+ });
860
+ if (enableAI) {
861
+ const aiProvider = await select4({
862
+ message: "\u9009\u62E9 AI \u63D0\u4F9B\u5546:",
863
+ choices: [
864
+ {
865
+ name: "GitHub Models\uFF08\u514D\u8D39\uFF0C\u63A8\u8350\uFF09",
866
+ value: "github",
867
+ description: "\u4F7F\u7528 GitHub \u8D26\u53F7\uFF0C\u6BCF\u5929 150 \u6B21\u514D\u8D39"
868
+ },
869
+ {
870
+ name: "Groq\uFF08\u514D\u8D39\uFF09",
871
+ value: "groq",
872
+ description: "\u9700\u8981\u6CE8\u518C\uFF0C\u6BCF\u5929 14,400 \u6B21\u514D\u8D39"
873
+ },
874
+ {
875
+ name: "OpenAI\uFF08\u4ED8\u8D39\uFF09",
876
+ value: "openai",
877
+ description: "\u9700\u8981\u4ED8\u8D39 API key"
878
+ },
879
+ {
880
+ name: "Claude\uFF08\u4ED8\u8D39\uFF09",
881
+ value: "claude",
882
+ description: "\u9700\u8981\u4ED8\u8D39 API key"
883
+ },
884
+ {
885
+ name: "Ollama\uFF08\u672C\u5730\uFF09",
886
+ value: "ollama",
887
+ description: "\u9700\u8981\u5B89\u88C5 Ollama"
888
+ }
889
+ ],
890
+ theme
891
+ });
892
+ const useBuiltinKey = await select4({
893
+ message: "API Key \u914D\u7F6E:",
894
+ choices: [
895
+ {
896
+ name: "\u4F7F\u7528\u5185\u7F6E Key\uFF08\u5F00\u7BB1\u5373\u7528\uFF09",
897
+ value: true,
898
+ description: "\u4F7F\u7528\u5DE5\u5177\u5185\u7F6E\u7684 API key\uFF0C\u5171\u4EAB\u9650\u989D"
899
+ },
900
+ {
901
+ name: "\u4F7F\u7528\u81EA\u5DF1\u7684 Key\uFF08\u63A8\u8350\uFF09",
902
+ value: false,
903
+ description: "\u914D\u7F6E\u81EA\u5DF1\u7684 API key\uFF0C\u72EC\u4EAB\u9650\u989D"
904
+ }
905
+ ],
906
+ theme
907
+ });
908
+ let apiKey = "";
909
+ if (!useBuiltinKey) {
910
+ apiKey = await input3({
911
+ message: `\u8F93\u5165\u4F60\u7684 ${aiProvider === "github" ? "GitHub Token" : "API Key"}:`,
912
+ validate: (value) => {
913
+ if (!value.trim()) return "API Key \u4E0D\u80FD\u4E3A\u7A7A";
914
+ return true;
915
+ },
916
+ theme
917
+ });
918
+ }
919
+ const language = await select4({
920
+ message: "\u751F\u6210\u7684 commit message \u8BED\u8A00:",
921
+ choices: [
922
+ { name: "\u4E2D\u6587", value: "zh-CN" },
923
+ { name: "English", value: "en-US" }
924
+ ],
925
+ theme
926
+ });
927
+ config2.aiCommit = {
928
+ enabled: true,
929
+ provider: aiProvider,
930
+ apiKey: apiKey || void 0,
931
+ language
932
+ };
933
+ const defaultModels = {
934
+ github: "gpt-4o-mini",
935
+ groq: "llama-3.1-8b-instant",
936
+ openai: "gpt-4o-mini",
937
+ claude: "claude-3-haiku-20240307",
938
+ ollama: "qwen2.5-coder:7b"
939
+ };
940
+ config2.aiCommit.model = defaultModels[aiProvider];
941
+ } else {
942
+ config2.aiCommit = {
943
+ enabled: false
944
+ };
945
+ }
946
+ divider();
837
947
  const content = JSON.stringify(config2, null, 2);
838
948
  writeFileSync2(CONFIG_FILE, content + "\n");
839
949
  console.log(colors.green(`\u2713 \u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ${CONFIG_FILE}`));
@@ -842,6 +952,23 @@ async function init() {
842
952
  "\n\u63D0\u793A: \u53EF\u4EE5\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u4FEE\u6539 commitEmojis \u6765\u81EA\u5B9A\u4E49\u5404\u7C7B\u578B\u7684 emoji"
843
953
  )
844
954
  );
955
+ if (config2.aiCommit?.enabled) {
956
+ console.log(
957
+ colors.dim(
958
+ "\u63D0\u793A: AI Commit \u5DF2\u542F\u7528\uFF0C\u8FD0\u884C 'gw c' \u65F6\u53EF\u4EE5\u9009\u62E9 AI \u81EA\u52A8\u751F\u6210 commit message"
959
+ )
960
+ );
961
+ if (!config2.aiCommit.apiKey) {
962
+ console.log(
963
+ colors.yellow(
964
+ "\n\u26A0\uFE0F \u5F53\u524D\u4F7F\u7528\u5185\u7F6E API key\uFF0C\u5EFA\u8BAE\u914D\u7F6E\u81EA\u5DF1\u7684 key \u4EE5\u83B7\u5F97\u66F4\u597D\u7684\u4F53\u9A8C"
965
+ )
966
+ );
967
+ console.log(
968
+ colors.dim(" \u83B7\u53D6\u65B9\u6CD5: https://github.com/settings/tokens/new")
969
+ );
970
+ }
971
+ }
845
972
  console.log(colors.dim("\n" + content));
846
973
  }
847
974
 
@@ -1081,8 +1208,254 @@ async function dropStash(index) {
1081
1208
 
1082
1209
  // src/commands/commit.ts
1083
1210
  import { execSync as execSync5 } from "child_process";
1084
- import { select as select6, input as input5, confirm as confirm2, checkbox } from "@inquirer/prompts";
1211
+ import { select as select6, input as input5, checkbox } from "@inquirer/prompts";
1085
1212
  import ora4 from "ora";
1213
+
1214
+ // src/ai-service.ts
1215
+ var AI_PROVIDERS = {
1216
+ github: {
1217
+ name: "GitHub Models",
1218
+ endpoint: "https://models.github.ai/inference/chat/completions",
1219
+ defaultModel: "gpt-4o-mini",
1220
+ free: true,
1221
+ needsKey: true
1222
+ },
1223
+ groq: {
1224
+ name: "Groq",
1225
+ endpoint: "https://api.groq.com/openai/v1/chat/completions",
1226
+ defaultModel: "llama-3.1-8b-instant",
1227
+ free: true,
1228
+ needsKey: true
1229
+ },
1230
+ openai: {
1231
+ name: "OpenAI",
1232
+ endpoint: "https://api.openai.com/v1/chat/completions",
1233
+ defaultModel: "gpt-4o-mini",
1234
+ free: false,
1235
+ needsKey: true
1236
+ },
1237
+ claude: {
1238
+ name: "Claude",
1239
+ endpoint: "https://api.anthropic.com/v1/messages",
1240
+ defaultModel: "claude-3-haiku-20240307",
1241
+ free: false,
1242
+ needsKey: true
1243
+ },
1244
+ ollama: {
1245
+ name: "Ollama",
1246
+ endpoint: "http://localhost:11434/api/generate",
1247
+ defaultModel: "qwen2.5-coder:7b",
1248
+ free: true,
1249
+ needsKey: false
1250
+ }
1251
+ };
1252
+ function getGitDiff() {
1253
+ try {
1254
+ const diff = execOutput("git diff --cached");
1255
+ if (!diff) {
1256
+ return execOutput("git diff");
1257
+ }
1258
+ return diff;
1259
+ } catch {
1260
+ return "";
1261
+ }
1262
+ }
1263
+ function buildPrompt(diff, language) {
1264
+ const isZh = language === "zh-CN";
1265
+ const systemPrompt = isZh ? `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 Git commit message \u751F\u6210\u52A9\u624B\u3002\u8BF7\u6839\u636E\u63D0\u4F9B\u7684 git diff \u751F\u6210\u7B26\u5408 Conventional Commits \u89C4\u8303\u7684 commit message\u3002
1266
+
1267
+ \u89C4\u5219\uFF1A
1268
+ 1. \u683C\u5F0F\uFF1A<type>(<scope>): <subject>
1269
+ 2. type \u5FC5\u987B\u662F\u4EE5\u4E0B\u4E4B\u4E00\uFF1Afeat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
1270
+ 3. scope \u662F\u53EF\u9009\u7684\uFF0C\u8868\u793A\u5F71\u54CD\u8303\u56F4
1271
+ 4. subject \u7528\u4E2D\u6587\u63CF\u8FF0\uFF0C\u7B80\u6D01\u660E\u4E86\uFF0C\u4E0D\u8D85\u8FC7 50 \u5B57
1272
+ 5. \u53EA\u8FD4\u56DE commit message\uFF0C\u4E0D\u8981\u6709\u5176\u4ED6\u89E3\u91CA
1273
+
1274
+ \u793A\u4F8B\uFF1A
1275
+ - feat(auth): \u6DFB\u52A0\u7528\u6237\u767B\u5F55\u529F\u80FD
1276
+ - fix(api): \u4FEE\u590D\u6570\u636E\u83B7\u53D6\u5931\u8D25\u7684\u95EE\u9898
1277
+ - docs(readme): \u66F4\u65B0\u5B89\u88C5\u8BF4\u660E` : `You are a professional Git commit message generator. Generate a commit message following Conventional Commits specification based on the provided git diff.
1278
+
1279
+ Rules:
1280
+ 1. Format: <type>(<scope>): <subject>
1281
+ 2. type must be one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
1282
+ 3. scope is optional, indicates the affected area
1283
+ 4. subject should be concise, no more than 50 characters
1284
+ 5. Return only the commit message, no explanations
1285
+
1286
+ Examples:
1287
+ - feat(auth): add user login functionality
1288
+ - fix(api): resolve data fetching failure
1289
+ - docs(readme): update installation guide`;
1290
+ const userPrompt = isZh ? `\u8BF7\u6839\u636E\u4EE5\u4E0B git diff \u751F\u6210 commit message\uFF1A
1291
+
1292
+ ${diff}` : `Generate a commit message based on the following git diff:
1293
+
1294
+ ${diff}`;
1295
+ return `${systemPrompt}
1296
+
1297
+ ${userPrompt}`;
1298
+ }
1299
+ async function callGitHubAPI(prompt, apiKey, model, maxTokens) {
1300
+ const response = await fetch(AI_PROVIDERS.github.endpoint, {
1301
+ method: "POST",
1302
+ headers: {
1303
+ Authorization: `Bearer ${apiKey}`,
1304
+ "Content-Type": "application/json"
1305
+ },
1306
+ body: JSON.stringify({
1307
+ model,
1308
+ messages: [{ role: "user", content: prompt }],
1309
+ max_tokens: maxTokens,
1310
+ temperature: 0.3
1311
+ })
1312
+ });
1313
+ if (!response.ok) {
1314
+ const error = await response.text();
1315
+ throw new Error(`GitHub Models API \u9519\u8BEF: ${response.status} ${error}`);
1316
+ }
1317
+ const data = await response.json();
1318
+ return data.choices[0]?.message?.content?.trim() || "";
1319
+ }
1320
+ async function callGroqAPI(prompt, apiKey, model, maxTokens) {
1321
+ const response = await fetch(AI_PROVIDERS.groq.endpoint, {
1322
+ method: "POST",
1323
+ headers: {
1324
+ Authorization: `Bearer ${apiKey}`,
1325
+ "Content-Type": "application/json"
1326
+ },
1327
+ body: JSON.stringify({
1328
+ model,
1329
+ messages: [{ role: "user", content: prompt }],
1330
+ max_tokens: maxTokens,
1331
+ temperature: 0.3
1332
+ })
1333
+ });
1334
+ if (!response.ok) {
1335
+ const error = await response.text();
1336
+ throw new Error(`Groq API \u9519\u8BEF: ${response.status} ${error}`);
1337
+ }
1338
+ const data = await response.json();
1339
+ return data.choices[0]?.message?.content?.trim() || "";
1340
+ }
1341
+ async function callOpenAIAPI(prompt, apiKey, model, maxTokens) {
1342
+ const response = await fetch(AI_PROVIDERS.openai.endpoint, {
1343
+ method: "POST",
1344
+ headers: {
1345
+ Authorization: `Bearer ${apiKey}`,
1346
+ "Content-Type": "application/json"
1347
+ },
1348
+ body: JSON.stringify({
1349
+ model,
1350
+ messages: [{ role: "user", content: prompt }],
1351
+ max_tokens: maxTokens,
1352
+ temperature: 0.3
1353
+ })
1354
+ });
1355
+ if (!response.ok) {
1356
+ const error = await response.text();
1357
+ throw new Error(`OpenAI API \u9519\u8BEF: ${response.status} ${error}`);
1358
+ }
1359
+ const data = await response.json();
1360
+ return data.choices[0]?.message?.content?.trim() || "";
1361
+ }
1362
+ async function callClaudeAPI(prompt, apiKey, model, maxTokens) {
1363
+ const response = await fetch(AI_PROVIDERS.claude.endpoint, {
1364
+ method: "POST",
1365
+ headers: {
1366
+ "x-api-key": apiKey,
1367
+ "anthropic-version": "2023-06-01",
1368
+ "Content-Type": "application/json"
1369
+ },
1370
+ body: JSON.stringify({
1371
+ model,
1372
+ messages: [{ role: "user", content: prompt }],
1373
+ max_tokens: maxTokens,
1374
+ temperature: 0.3
1375
+ })
1376
+ });
1377
+ if (!response.ok) {
1378
+ const error = await response.text();
1379
+ throw new Error(`Claude API \u9519\u8BEF: ${response.status} ${error}`);
1380
+ }
1381
+ const data = await response.json();
1382
+ return data.content[0]?.text?.trim() || "";
1383
+ }
1384
+ async function callOllamaAPI(prompt, model, maxTokens) {
1385
+ try {
1386
+ const response = await fetch(AI_PROVIDERS.ollama.endpoint, {
1387
+ method: "POST",
1388
+ headers: { "Content-Type": "application/json" },
1389
+ body: JSON.stringify({
1390
+ model,
1391
+ prompt,
1392
+ stream: false,
1393
+ options: {
1394
+ num_predict: maxTokens,
1395
+ temperature: 0.3
1396
+ }
1397
+ })
1398
+ });
1399
+ if (!response.ok) {
1400
+ throw new Error(`Ollama \u672A\u8FD0\u884C\u6216\u6A21\u578B\u672A\u5B89\u88C5`);
1401
+ }
1402
+ const data = await response.json();
1403
+ return data.response?.trim() || "";
1404
+ } catch (error) {
1405
+ throw new Error(
1406
+ `Ollama \u8FDE\u63A5\u5931\u8D25\u3002\u8BF7\u786E\u4FDD\uFF1A
1407
+ 1. \u5DF2\u5B89\u88C5 Ollama (https://ollama.com)
1408
+ 2. \u8FD0\u884C 'ollama serve'
1409
+ 3. \u4E0B\u8F7D\u6A21\u578B 'ollama pull ${model}'`
1410
+ );
1411
+ }
1412
+ }
1413
+ async function generateAICommitMessage(config2) {
1414
+ const aiConfig = config2.aiCommit || {};
1415
+ const provider = aiConfig.provider || "groq";
1416
+ const language = aiConfig.language || "zh-CN";
1417
+ const maxTokens = aiConfig.maxTokens || 200;
1418
+ const diff = getGitDiff();
1419
+ if (!diff) {
1420
+ throw new Error("\u6CA1\u6709\u68C0\u6D4B\u5230\u4EE3\u7801\u66F4\u6539");
1421
+ }
1422
+ const maxDiffLength = 4e3;
1423
+ const truncatedDiff = diff.length > maxDiffLength ? diff.slice(0, maxDiffLength) + "\n..." : diff;
1424
+ const prompt = buildPrompt(truncatedDiff, language);
1425
+ const providerInfo = AI_PROVIDERS[provider];
1426
+ if (!providerInfo) {
1427
+ throw new Error(`\u4E0D\u652F\u6301\u7684 AI \u63D0\u4F9B\u5546: ${provider}`);
1428
+ }
1429
+ const model = aiConfig.model || providerInfo.defaultModel;
1430
+ const apiKey = aiConfig.apiKey || "";
1431
+ if (providerInfo.needsKey && !apiKey) {
1432
+ throw new Error(
1433
+ `${providerInfo.name} \u9700\u8981 API key\u3002\u8BF7\u8FD0\u884C 'gw init' \u914D\u7F6E AI commit\uFF0C\u6216\u5728 .gwrc.json \u4E2D\u8BBE\u7F6E aiCommit.apiKey`
1434
+ );
1435
+ }
1436
+ switch (provider) {
1437
+ case "github":
1438
+ return await callGitHubAPI(prompt, apiKey, model, maxTokens);
1439
+ case "groq":
1440
+ return await callGroqAPI(prompt, apiKey, model, maxTokens);
1441
+ case "openai":
1442
+ return await callOpenAIAPI(prompt, apiKey, model, maxTokens);
1443
+ case "claude":
1444
+ return await callClaudeAPI(prompt, apiKey, model, maxTokens);
1445
+ case "ollama":
1446
+ return await callOllamaAPI(prompt, model, maxTokens);
1447
+ default:
1448
+ throw new Error(`\u4E0D\u652F\u6301\u7684 AI \u63D0\u4F9B\u5546: ${provider}`);
1449
+ }
1450
+ }
1451
+ function isAICommitAvailable(config2) {
1452
+ const aiConfig = config2.aiCommit;
1453
+ if (!aiConfig) return true;
1454
+ if (aiConfig.enabled === false) return false;
1455
+ return true;
1456
+ }
1457
+
1458
+ // src/commands/commit.ts
1086
1459
  var DEFAULT_COMMIT_TYPES = [
1087
1460
  { type: "feat", emoji: "\u2728", description: "\u65B0\u529F\u80FD" },
1088
1461
  { type: "fix", emoji: "\u{1F41B}", description: "\u4FEE\u590D Bug" },
@@ -1185,6 +1558,94 @@ async function commit() {
1185
1558
  }
1186
1559
  divider();
1187
1560
  }
1561
+ const aiAvailable = isAICommitAvailable(config2);
1562
+ let commitMode = "manual";
1563
+ if (aiAvailable) {
1564
+ commitMode = await select6({
1565
+ message: "\u9009\u62E9 commit \u65B9\u5F0F:",
1566
+ choices: [
1567
+ {
1568
+ name: "\u{1F916} AI \u81EA\u52A8\u751F\u6210 commit message",
1569
+ value: "ai",
1570
+ description: "\u4F7F\u7528 AI \u5206\u6790\u4EE3\u7801\u53D8\u66F4\u81EA\u52A8\u751F\u6210"
1571
+ },
1572
+ {
1573
+ name: "\u270D\uFE0F \u624B\u52A8\u7F16\u5199 commit message",
1574
+ value: "manual",
1575
+ description: "\u4F20\u7EDF\u7684\u4EA4\u4E92\u5F0F\u8F93\u5165\u65B9\u5F0F"
1576
+ }
1577
+ ],
1578
+ theme
1579
+ });
1580
+ }
1581
+ let message;
1582
+ if (commitMode === "ai") {
1583
+ const spinner2 = ora4("AI \u6B63\u5728\u5206\u6790\u4EE3\u7801\u53D8\u66F4...").start();
1584
+ try {
1585
+ const aiMessage = await generateAICommitMessage(config2);
1586
+ spinner2.succeed("AI \u751F\u6210\u5B8C\u6210");
1587
+ console.log("");
1588
+ console.log("AI \u751F\u6210\u7684 commit message:");
1589
+ console.log(colors.green(aiMessage));
1590
+ divider();
1591
+ const useAI = await select6({
1592
+ message: "\u4F7F\u7528\u8FD9\u4E2A commit message?",
1593
+ choices: [
1594
+ { name: "\u2705 \u4F7F\u7528", value: true },
1595
+ { name: "\u274C \u4E0D\u4F7F\u7528\uFF0C\u5207\u6362\u5230\u624B\u52A8\u6A21\u5F0F", value: false }
1596
+ ],
1597
+ theme
1598
+ });
1599
+ if (useAI) {
1600
+ message = aiMessage;
1601
+ } else {
1602
+ spinner2.info("\u5207\u6362\u5230\u624B\u52A8\u6A21\u5F0F");
1603
+ commitMode = "manual";
1604
+ }
1605
+ } catch (error) {
1606
+ spinner2.fail("AI \u751F\u6210\u5931\u8D25");
1607
+ console.log(
1608
+ colors.red(error instanceof Error ? error.message : String(error))
1609
+ );
1610
+ console.log(colors.yellow("\n\u5207\u6362\u5230\u624B\u52A8\u6A21\u5F0F..."));
1611
+ divider();
1612
+ commitMode = "manual";
1613
+ }
1614
+ }
1615
+ if (commitMode === "manual") {
1616
+ message = await buildManualCommitMessage(config2);
1617
+ }
1618
+ divider();
1619
+ console.log("\u63D0\u4EA4\u4FE1\u606F\u9884\u89C8:");
1620
+ console.log(colors.green(message));
1621
+ divider();
1622
+ const shouldCommit = await select6({
1623
+ message: "\u786E\u8BA4\u63D0\u4EA4?",
1624
+ choices: [
1625
+ { name: "\u2705 \u786E\u8BA4\u63D0\u4EA4", value: true },
1626
+ { name: "\u274C \u53D6\u6D88", value: false }
1627
+ ],
1628
+ theme
1629
+ });
1630
+ if (!shouldCommit) {
1631
+ console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
1632
+ return;
1633
+ }
1634
+ const spinner = ora4("\u6B63\u5728\u63D0\u4EA4...").start();
1635
+ try {
1636
+ const escapedMessage = message.replace(/"/g, '\\"');
1637
+ execSync5(`git commit -m "${escapedMessage}"`, { stdio: "pipe" });
1638
+ spinner.succeed("\u63D0\u4EA4\u6210\u529F");
1639
+ const commitHash = execOutput("git rev-parse --short HEAD");
1640
+ console.log(colors.dim(`commit: ${commitHash}`));
1641
+ } catch (error) {
1642
+ spinner.fail("\u63D0\u4EA4\u5931\u8D25");
1643
+ if (error instanceof Error) {
1644
+ console.log(colors.red(error.message));
1645
+ }
1646
+ }
1647
+ }
1648
+ async function buildManualCommitMessage(config2) {
1188
1649
  const commitTypes = getCommitTypes(config2);
1189
1650
  const typeChoice = await select6({
1190
1651
  message: "\u9009\u62E9\u63D0\u4EA4\u7C7B\u578B:",
@@ -1211,9 +1672,12 @@ async function commit() {
1211
1672
  message: "\u8F93\u5165\u8BE6\u7EC6\u63CF\u8FF0 (\u53EF\u8DF3\u8FC7):",
1212
1673
  theme
1213
1674
  });
1214
- const hasBreaking = await confirm2({
1675
+ const hasBreaking = await select6({
1215
1676
  message: "\u662F\u5426\u5305\u542B\u7834\u574F\u6027\u53D8\u66F4 (BREAKING CHANGE)?",
1216
- default: false,
1677
+ choices: [
1678
+ { name: "\u5426", value: false },
1679
+ { name: "\u662F", value: true }
1680
+ ],
1217
1681
  theme
1218
1682
  });
1219
1683
  let breakingDesc = "";
@@ -1251,32 +1715,7 @@ BREAKING CHANGE: ${breakingDesc}`;
1251
1715
  ${issues}`;
1252
1716
  }
1253
1717
  }
1254
- divider();
1255
- console.log("\u63D0\u4EA4\u4FE1\u606F\u9884\u89C8:");
1256
- console.log(colors.green(message));
1257
- divider();
1258
- const shouldCommit = await confirm2({
1259
- message: "\u786E\u8BA4\u63D0\u4EA4?",
1260
- default: true,
1261
- theme
1262
- });
1263
- if (!shouldCommit) {
1264
- console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
1265
- return;
1266
- }
1267
- const spinner = ora4("\u6B63\u5728\u63D0\u4EA4...").start();
1268
- try {
1269
- const escapedMessage = message.replace(/"/g, '\\"');
1270
- execSync5(`git commit -m "${escapedMessage}"`, { stdio: "pipe" });
1271
- spinner.succeed("\u63D0\u4EA4\u6210\u529F");
1272
- const commitHash = execOutput("git rev-parse --short HEAD");
1273
- console.log(colors.dim(`commit: ${commitHash}`));
1274
- } catch (error) {
1275
- spinner.fail("\u63D0\u4EA4\u5931\u8D25");
1276
- if (error instanceof Error) {
1277
- console.log(colors.red(error.message));
1278
- }
1279
- }
1718
+ return message;
1280
1719
  }
1281
1720
 
1282
1721
  // src/commands/help.ts
@@ -1437,11 +1876,11 @@ async function performUpdate(packageName) {
1437
1876
  }).start();
1438
1877
  try {
1439
1878
  try {
1440
- execSync6("npm uninstall -g git-workflow", {
1879
+ execSync6(`npm uninstall -g ${packageName}`, {
1441
1880
  encoding: "utf-8",
1442
1881
  stdio: ["pipe", "pipe", "pipe"]
1443
1882
  });
1444
- spinner.text = "\u5DF2\u5378\u8F7D\u65E7\u7248\u672C\uFF0C\u6B63\u5728\u5B89\u88C5\u65B0\u7248\u672C...";
1883
+ spinner.text = "\u6B63\u5728\u5B89\u88C5\u65B0\u7248\u672C...";
1445
1884
  } catch {
1446
1885
  }
1447
1886
  execSync6(`npm install -g ${packageName}`, {
@@ -1450,17 +1889,31 @@ async function performUpdate(packageName) {
1450
1889
  });
1451
1890
  spinner.succeed(colors.green("\u66F4\u65B0\u6210\u529F\uFF01"));
1452
1891
  console.log("");
1453
- console.log(colors.cyan(" \u63D0\u793A: \u8BF7\u91CD\u65B0\u8FD0\u884C\u547D\u4EE4\u4EE5\u4F7F\u7528\u65B0\u7248\u672C"));
1454
- console.log("");
1892
+ console.log(
1893
+ boxen(
1894
+ [
1895
+ colors.bold("\u2728 \u66F4\u65B0\u5B8C\u6210\uFF01"),
1896
+ "",
1897
+ colors.dim("\u8BF7\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u5237\u65B0\u5E76\u4F7F\u7528\u65B0\u7248\u672C:"),
1898
+ "",
1899
+ colors.yellow(" hash -r && gw --version"),
1900
+ "",
1901
+ colors.dim("\u6216\u8005\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF")
1902
+ ].join("\n"),
1903
+ {
1904
+ padding: 1,
1905
+ margin: { top: 0, bottom: 1, left: 2, right: 2 },
1906
+ borderStyle: "round",
1907
+ borderColor: "green",
1908
+ align: "left"
1909
+ }
1910
+ )
1911
+ );
1455
1912
  process.exit(0);
1456
1913
  } catch (error) {
1457
1914
  spinner.fail(colors.red("\u66F4\u65B0\u5931\u8D25"));
1458
1915
  console.log("");
1459
1916
  console.log(colors.dim(" \u4F60\u53EF\u4EE5\u624B\u52A8\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0:"));
1460
- console.log(colors.yellow(" # \u5982\u679C\u4E4B\u524D\u5B89\u88C5\u8FC7\u65E7\u7248\u672C\uFF0C\u5148\u5378\u8F7D:"));
1461
- console.log(colors.cyan(" npm uninstall -g git-workflow"));
1462
- console.log("");
1463
- console.log(colors.yellow(" # \u7136\u540E\u5B89\u88C5\u65B0\u7248\u672C:"));
1464
1917
  console.log(colors.cyan(` npm install -g ${packageName}`));
1465
1918
  console.log("");
1466
1919
  }
@@ -1488,12 +1941,29 @@ function writeCache(cache) {
1488
1941
  // src/index.ts
1489
1942
  process.on("uncaughtException", (err) => {
1490
1943
  if (err instanceof ExitPromptError) {
1944
+ console.log("");
1491
1945
  process.exit(0);
1492
1946
  }
1493
1947
  console.error(err);
1494
1948
  process.exit(1);
1495
1949
  });
1496
- var version = true ? "0.2.5" : "0.0.0-dev";
1950
+ process.on("unhandledRejection", (reason) => {
1951
+ if (reason instanceof ExitPromptError) {
1952
+ console.log("");
1953
+ process.exit(0);
1954
+ }
1955
+ console.error("\u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:", reason);
1956
+ process.exit(1);
1957
+ });
1958
+ process.on("SIGINT", () => {
1959
+ console.log("");
1960
+ process.exit(0);
1961
+ });
1962
+ process.on("SIGTERM", () => {
1963
+ console.log("");
1964
+ process.exit(0);
1965
+ });
1966
+ var version = true ? "0.2.7" : "0.0.0-dev";
1497
1967
  async function mainMenu() {
1498
1968
  await checkForUpdates(version, "@zjex/git-workflow");
1499
1969
  console.log(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zjex/git-workflow",
3
- "version": "0.2.5",
4
- "description": "个人常用的 Git 工作流工具,快速创建规范的开发分支和管理 Tag",
3
+ "version": "0.2.7",
4
+ "description": "🚀 极简的 Git 工作流 CLI 工具,让分支管理和版本发布变得轻松愉快",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "git-flow": "./dist/index.js",