@zjex/git-workflow 0.2.16 → 0.2.18

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
@@ -75,6 +75,7 @@ import ora from "ora";
75
75
  // src/config.ts
76
76
  import { existsSync, readFileSync } from "fs";
77
77
  import { join } from "path";
78
+ import { homedir } from "os";
78
79
  var defaultConfig = {
79
80
  featurePrefix: "feature",
80
81
  hotfixPrefix: "hotfix",
@@ -108,19 +109,47 @@ function findConfigFile() {
108
109
  }
109
110
  return null;
110
111
  }
112
+ function findGlobalConfigFile() {
113
+ const globalConfigPath = join(homedir(), ".gwrc.json");
114
+ return existsSync(globalConfigPath) ? globalConfigPath : null;
115
+ }
111
116
  function loadConfig() {
112
- const configPath = findConfigFile();
113
- if (!configPath) {
114
- return defaultConfig;
117
+ let config2 = { ...defaultConfig };
118
+ const globalConfigPath = findGlobalConfigFile();
119
+ if (globalConfigPath) {
120
+ try {
121
+ const content = readFileSync(globalConfigPath, "utf-8");
122
+ const globalConfig = JSON.parse(content);
123
+ config2 = {
124
+ ...config2,
125
+ ...globalConfig,
126
+ aiCommit: {
127
+ ...config2.aiCommit,
128
+ ...globalConfig.aiCommit
129
+ }
130
+ };
131
+ } catch (e) {
132
+ console.warn(`\u5168\u5C40\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25: ${globalConfigPath}`);
133
+ }
115
134
  }
116
- try {
117
- const content = readFileSync(configPath, "utf-8");
118
- const userConfig = JSON.parse(content);
119
- return { ...defaultConfig, ...userConfig };
120
- } catch (e) {
121
- console.warn(`\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25: ${configPath}`);
122
- return defaultConfig;
135
+ const projectConfigPath = findConfigFile();
136
+ if (projectConfigPath) {
137
+ try {
138
+ const content = readFileSync(projectConfigPath, "utf-8");
139
+ const projectConfig = JSON.parse(content);
140
+ config2 = {
141
+ ...config2,
142
+ ...projectConfig,
143
+ aiCommit: {
144
+ ...config2.aiCommit,
145
+ ...projectConfig.aiCommit
146
+ }
147
+ };
148
+ } catch (e) {
149
+ console.warn(`\u9879\u76EE\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25: ${projectConfigPath}`);
150
+ }
123
151
  }
152
+ return config2;
124
153
  }
125
154
  var config = null;
126
155
  function getConfig() {
@@ -860,8 +889,9 @@ async function release() {
860
889
 
861
890
  // src/commands/init.ts
862
891
  import { existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
892
+ import { join as join2 } from "path";
893
+ import { homedir as homedir2 } from "os";
863
894
  import { select as select4, input as input3 } from "@inquirer/prompts";
864
- var CONFIG_FILE = ".gwrc.json";
865
895
  var DEFAULT_COMMIT_EMOJIS = {
866
896
  feat: "\u2728",
867
897
  fix: "\u{1F41B}",
@@ -876,9 +906,30 @@ var DEFAULT_COMMIT_EMOJIS = {
876
906
  revert: "\u23EA"
877
907
  };
878
908
  async function init() {
879
- if (existsSync2(CONFIG_FILE)) {
909
+ console.log("");
910
+ console.log(colors.bold("\u2699\uFE0F \u521D\u59CB\u5316 git-workflow \u914D\u7F6E"));
911
+ console.log("");
912
+ const configScope = await select4({
913
+ message: "\u9009\u62E9\u914D\u7F6E\u8303\u56F4:",
914
+ choices: [
915
+ {
916
+ name: "\u5168\u5C40\u914D\u7F6E\uFF08\u6240\u6709\u9879\u76EE\u751F\u6548\uFF09",
917
+ value: "global",
918
+ description: "\u4FDD\u5B58\u5230 ~/.gwrc.json\uFF0C\u6240\u6709\u9879\u76EE\u90FD\u4F1A\u4F7F\u7528\u6B64\u914D\u7F6E"
919
+ },
920
+ {
921
+ name: "\u9879\u76EE\u914D\u7F6E\uFF08\u4EC5\u5F53\u524D\u9879\u76EE\uFF09",
922
+ value: "project",
923
+ description: "\u4FDD\u5B58\u5230\u5F53\u524D\u76EE\u5F55 .gwrc.json\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u4F7F\u7528"
924
+ }
925
+ ],
926
+ theme
927
+ });
928
+ const isGlobal = configScope === "global";
929
+ const configFile = isGlobal ? join2(homedir2(), ".gwrc.json") : ".gwrc.json";
930
+ if (existsSync2(configFile)) {
880
931
  const overwrite = await select4({
881
- message: `${CONFIG_FILE} \u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6?`,
932
+ message: `${isGlobal ? "\u5168\u5C40" : "\u9879\u76EE"}\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6?`,
882
933
  choices: [
883
934
  { name: "\u5426\uFF0C\u53D6\u6D88", value: false },
884
935
  { name: "\u662F\uFF0C\u8986\u76D6", value: true }
@@ -903,13 +954,13 @@ async function init() {
903
954
  default: "feature",
904
955
  theme
905
956
  });
906
- if (featurePrefix !== "feature") config2.featurePrefix = featurePrefix;
957
+ config2.featurePrefix = featurePrefix;
907
958
  const hotfixPrefix = await input3({
908
959
  message: "Hotfix \u5206\u652F\u524D\u7F00:",
909
960
  default: "hotfix",
910
961
  theme
911
962
  });
912
- if (hotfixPrefix !== "hotfix") config2.hotfixPrefix = hotfixPrefix;
963
+ config2.hotfixPrefix = hotfixPrefix;
913
964
  divider();
914
965
  const requireId = await select4({
915
966
  message: "\u662F\u5426\u8981\u6C42\u5FC5\u586B ID (Story ID / Issue ID)?",
@@ -919,19 +970,19 @@ async function init() {
919
970
  ],
920
971
  theme
921
972
  });
922
- if (requireId) config2.requireId = true;
973
+ config2.requireId = requireId;
923
974
  const featureIdLabel = await input3({
924
975
  message: "Feature \u5206\u652F ID \u6807\u7B7E:",
925
976
  default: "Story ID",
926
977
  theme
927
978
  });
928
- if (featureIdLabel !== "Story ID") config2.featureIdLabel = featureIdLabel;
979
+ config2.featureIdLabel = featureIdLabel;
929
980
  const hotfixIdLabel = await input3({
930
981
  message: "Hotfix \u5206\u652F ID \u6807\u7B7E:",
931
982
  default: "Issue ID",
932
983
  theme
933
984
  });
934
- if (hotfixIdLabel !== "Issue ID") config2.hotfixIdLabel = hotfixIdLabel;
985
+ config2.hotfixIdLabel = hotfixIdLabel;
935
986
  divider();
936
987
  const defaultTagPrefix = await input3({
937
988
  message: "\u9ED8\u8BA4 Tag \u524D\u7F00 (\u7559\u7A7A\u5219\u6BCF\u6B21\u9009\u62E9):",
@@ -947,8 +998,11 @@ async function init() {
947
998
  ],
948
999
  theme
949
1000
  });
950
- if (autoPushChoice === "yes") config2.autoPush = true;
951
- if (autoPushChoice === "no") config2.autoPush = false;
1001
+ if (autoPushChoice === "yes") {
1002
+ config2.autoPush = true;
1003
+ } else if (autoPushChoice === "no") {
1004
+ config2.autoPush = false;
1005
+ }
952
1006
  divider();
953
1007
  const autoStage = await select4({
954
1008
  message: "Commit \u65F6\u662F\u5426\u81EA\u52A8\u6682\u5B58\u6240\u6709\u66F4\u6539?",
@@ -958,7 +1012,7 @@ async function init() {
958
1012
  ],
959
1013
  theme
960
1014
  });
961
- if (!autoStage) config2.autoStage = false;
1015
+ config2.autoStage = autoStage;
962
1016
  const useEmoji = await select4({
963
1017
  message: "Commit \u65F6\u662F\u5426\u4F7F\u7528 emoji?",
964
1018
  choices: [
@@ -967,7 +1021,7 @@ async function init() {
967
1021
  ],
968
1022
  theme
969
1023
  });
970
- if (!useEmoji) config2.useEmoji = false;
1024
+ config2.useEmoji = useEmoji;
971
1025
  config2.commitEmojis = DEFAULT_COMMIT_EMOJIS;
972
1026
  divider();
973
1027
  console.log(
@@ -990,11 +1044,6 @@ async function init() {
990
1044
  value: "github",
991
1045
  description: "\u4F7F\u7528 GitHub \u8D26\u53F7\uFF0C\u6BCF\u5929 150 \u6B21\u514D\u8D39"
992
1046
  },
993
- {
994
- name: "Groq\uFF08\u514D\u8D39\uFF09",
995
- value: "groq",
996
- description: "\u9700\u8981\u6CE8\u518C\uFF0C\u6BCF\u5929 14,400 \u6B21\u514D\u8D39"
997
- },
998
1047
  {
999
1048
  name: "OpenAI\uFF08\u4ED8\u8D39\uFF09",
1000
1049
  value: "openai",
@@ -1013,26 +1062,36 @@ async function init() {
1013
1062
  ],
1014
1063
  theme
1015
1064
  });
1016
- const useBuiltinKey = await select4({
1017
- message: "API Key \u914D\u7F6E:",
1018
- choices: [
1019
- {
1020
- name: "\u4F7F\u7528\u5185\u7F6E Key\uFF08\u5F00\u7BB1\u5373\u7528\uFF09",
1021
- value: true,
1022
- description: "\u4F7F\u7528\u5DE5\u5177\u5185\u7F6E\u7684 API key\uFF0C\u5171\u4EAB\u9650\u989D"
1023
- },
1024
- {
1025
- name: "\u4F7F\u7528\u81EA\u5DF1\u7684 Key\uFF08\u63A8\u8350\uFF09",
1026
- value: false,
1027
- description: "\u914D\u7F6E\u81EA\u5DF1\u7684 API key\uFF0C\u72EC\u4EAB\u9650\u989D"
1028
- }
1029
- ],
1030
- theme
1031
- });
1032
1065
  let apiKey = "";
1033
- if (!useBuiltinKey) {
1066
+ if (aiProvider === "github") {
1067
+ console.log("");
1068
+ console.log(colors.cyan("\u{1F4A1} \u5982\u4F55\u83B7\u53D6 GitHub Token:"));
1069
+ console.log(
1070
+ colors.dim(" 1. \u8BBF\u95EE: https://github.com/settings/tokens/new")
1071
+ );
1072
+ console.log(colors.dim(" 2. \u52FE\u9009 'repo' \u6743\u9650"));
1073
+ console.log(colors.dim(" 3. \u751F\u6210\u5E76\u590D\u5236 token"));
1074
+ console.log("");
1034
1075
  apiKey = await input3({
1035
- message: `\u8F93\u5165\u4F60\u7684 ${aiProvider === "github" ? "GitHub Token" : "API Key"}:`,
1076
+ message: "\u8F93\u5165\u4F60\u7684 GitHub Token:",
1077
+ validate: (value) => {
1078
+ if (!value.trim()) return "GitHub Token \u4E0D\u80FD\u4E3A\u7A7A";
1079
+ return true;
1080
+ },
1081
+ theme
1082
+ });
1083
+ } else if (aiProvider !== "ollama") {
1084
+ console.log("");
1085
+ if (aiProvider === "openai") {
1086
+ console.log(colors.cyan("\u{1F4A1} \u5982\u4F55\u83B7\u53D6 OpenAI API Key:"));
1087
+ console.log(colors.dim(" \u8BBF\u95EE: https://platform.openai.com/api-keys"));
1088
+ } else {
1089
+ console.log(colors.cyan("\u{1F4A1} \u5982\u4F55\u83B7\u53D6 Claude API Key:"));
1090
+ console.log(colors.dim(" \u8BBF\u95EE: https://console.anthropic.com/"));
1091
+ }
1092
+ console.log("");
1093
+ apiKey = await input3({
1094
+ message: `\u8F93\u5165\u4F60\u7684 ${aiProvider === "openai" ? "OpenAI API Key" : "Claude API Key"}:`,
1036
1095
  validate: (value) => {
1037
1096
  if (!value.trim()) return "API Key \u4E0D\u80FD\u4E3A\u7A7A";
1038
1097
  return true;
@@ -1056,7 +1115,6 @@ async function init() {
1056
1115
  };
1057
1116
  const defaultModels = {
1058
1117
  github: "gpt-4o-mini",
1059
- groq: "llama-3.1-8b-instant",
1060
1118
  openai: "gpt-4o-mini",
1061
1119
  claude: "claude-3-haiku-20240307",
1062
1120
  ollama: "qwen2.5-coder:7b"
@@ -1069,13 +1127,30 @@ async function init() {
1069
1127
  }
1070
1128
  divider();
1071
1129
  const content = JSON.stringify(config2, null, 2);
1072
- writeFileSync2(CONFIG_FILE, content + "\n");
1073
- console.log(colors.green(`\u2713 \u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ${CONFIG_FILE}`));
1130
+ writeFileSync2(configFile, content + "\n");
1074
1131
  console.log(
1075
- colors.dim(
1076
- "\n\u63D0\u793A: \u53EF\u4EE5\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u4FEE\u6539 commitEmojis \u6765\u81EA\u5B9A\u4E49\u5404\u7C7B\u578B\u7684 emoji"
1132
+ colors.green(
1133
+ `\u2713 \u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ${isGlobal ? "\u5168\u5C40\u914D\u7F6E\u6587\u4EF6" : "\u9879\u76EE\u914D\u7F6E\u6587\u4EF6"}: ${configFile}`
1077
1134
  )
1078
1135
  );
1136
+ if (isGlobal) {
1137
+ console.log("");
1138
+ console.log(colors.cyan("\u{1F4A1} \u63D0\u793A:"));
1139
+ console.log(
1140
+ colors.dim(" \u2022 \u5168\u5C40\u914D\u7F6E\u5BF9\u6240\u6709\u9879\u76EE\u751F\u6548\uFF0C\u65E0\u9700\u5728\u6BCF\u4E2A\u9879\u76EE\u4E2D\u91CD\u590D\u914D\u7F6E")
1141
+ );
1142
+ console.log(
1143
+ colors.dim(" \u2022 \u5982\u9700\u4E3A\u7279\u5B9A\u9879\u76EE\u81EA\u5B9A\u4E49\u914D\u7F6E\uFF0C\u53EF\u5728\u9879\u76EE\u4E2D\u8FD0\u884C gw init")
1144
+ );
1145
+ console.log(colors.dim(" \u2022 \u9879\u76EE\u914D\u7F6E\u4F1A\u8986\u76D6\u5168\u5C40\u914D\u7F6E"));
1146
+ } else {
1147
+ console.log("");
1148
+ console.log(
1149
+ colors.dim(
1150
+ "\u63D0\u793A: \u53EF\u4EE5\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u4FEE\u6539 commitEmojis \u6765\u81EA\u5B9A\u4E49\u5404\u7C7B\u578B\u7684 emoji"
1151
+ )
1152
+ );
1153
+ }
1079
1154
  if (config2.aiCommit?.enabled) {
1080
1155
  console.log(
1081
1156
  colors.dim(
@@ -1344,13 +1419,6 @@ var AI_PROVIDERS = {
1344
1419
  free: true,
1345
1420
  needsKey: true
1346
1421
  },
1347
- groq: {
1348
- name: "Groq",
1349
- endpoint: "https://api.groq.com/openai/v1/chat/completions",
1350
- defaultModel: "llama-3.1-8b-instant",
1351
- free: true,
1352
- needsKey: true
1353
- },
1354
1422
  openai: {
1355
1423
  name: "OpenAI",
1356
1424
  endpoint: "https://api.openai.com/v1/chat/completions",
@@ -1393,7 +1461,8 @@ function buildPrompt(diff, language) {
1393
1461
  2. type \u5FC5\u987B\u662F\u4EE5\u4E0B\u4E4B\u4E00\uFF1Afeat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
1394
1462
  3. scope \u662F\u53EF\u9009\u7684\uFF0C\u8868\u793A\u5F71\u54CD\u8303\u56F4
1395
1463
  4. subject \u7528\u4E2D\u6587\u63CF\u8FF0\uFF0C\u7B80\u6D01\u660E\u4E86\uFF0C\u4E0D\u8D85\u8FC7 50 \u5B57
1396
- 5. \u53EA\u8FD4\u56DE commit message\uFF0C\u4E0D\u8981\u6709\u5176\u4ED6\u89E3\u91CA
1464
+ 5. \u53EA\u8FD4\u56DE\u4E00\u6761 commit message\uFF0C\u5373\u4F7F\u6709\u591A\u4E2A\u6587\u4EF6\u6539\u52A8\u4E5F\u8981\u603B\u7ED3\u6210\u4E00\u6761
1465
+ 6. \u4E0D\u8981\u6709\u5176\u4ED6\u89E3\u91CA\u6216\u591A\u4F59\u5185\u5BB9
1397
1466
 
1398
1467
  \u793A\u4F8B\uFF1A
1399
1468
  - feat(auth): \u6DFB\u52A0\u7528\u6237\u767B\u5F55\u529F\u80FD
@@ -1405,7 +1474,8 @@ Rules:
1405
1474
  2. type must be one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
1406
1475
  3. scope is optional, indicates the affected area
1407
1476
  4. subject should be concise, no more than 50 characters
1408
- 5. Return only the commit message, no explanations
1477
+ 5. Return only ONE commit message, even if multiple files are changed, summarize into one message
1478
+ 6. No explanations or extra content
1409
1479
 
1410
1480
  Examples:
1411
1481
  - feat(auth): add user login functionality
@@ -1441,27 +1511,6 @@ async function callGitHubAPI(prompt, apiKey, model, maxTokens) {
1441
1511
  const data = await response.json();
1442
1512
  return data.choices[0]?.message?.content?.trim() || "";
1443
1513
  }
1444
- async function callGroqAPI(prompt, apiKey, model, maxTokens) {
1445
- const response = await fetch(AI_PROVIDERS.groq.endpoint, {
1446
- method: "POST",
1447
- headers: {
1448
- Authorization: `Bearer ${apiKey}`,
1449
- "Content-Type": "application/json"
1450
- },
1451
- body: JSON.stringify({
1452
- model,
1453
- messages: [{ role: "user", content: prompt }],
1454
- max_tokens: maxTokens,
1455
- temperature: 0.3
1456
- })
1457
- });
1458
- if (!response.ok) {
1459
- const error = await response.text();
1460
- throw new Error(`Groq API \u9519\u8BEF: ${response.status} ${error}`);
1461
- }
1462
- const data = await response.json();
1463
- return data.choices[0]?.message?.content?.trim() || "";
1464
- }
1465
1514
  async function callOpenAIAPI(prompt, apiKey, model, maxTokens) {
1466
1515
  const response = await fetch(AI_PROVIDERS.openai.endpoint, {
1467
1516
  method: "POST",
@@ -1536,7 +1585,7 @@ async function callOllamaAPI(prompt, model, maxTokens) {
1536
1585
  }
1537
1586
  async function generateAICommitMessage(config2) {
1538
1587
  const aiConfig = config2.aiCommit || {};
1539
- const provider = aiConfig.provider || "groq";
1588
+ const provider = aiConfig.provider || "github";
1540
1589
  const language = aiConfig.language || "zh-CN";
1541
1590
  const maxTokens = aiConfig.maxTokens || 200;
1542
1591
  const diff = getGitDiff();
@@ -1560,8 +1609,6 @@ async function generateAICommitMessage(config2) {
1560
1609
  switch (provider) {
1561
1610
  case "github":
1562
1611
  return await callGitHubAPI(prompt, apiKey, model, maxTokens);
1563
- case "groq":
1564
- return await callGroqAPI(prompt, apiKey, model, maxTokens);
1565
1612
  case "openai":
1566
1613
  return await callOpenAIAPI(prompt, apiKey, model, maxTokens);
1567
1614
  case "claude":
@@ -1634,18 +1681,7 @@ function formatFileStatus(status) {
1634
1681
  async function commit() {
1635
1682
  const config2 = getConfig();
1636
1683
  let { staged, unstaged } = parseGitStatus();
1637
- if (staged.length === 0) {
1638
- if (unstaged.length === 0) {
1639
- console.log(colors.yellow("\u5DE5\u4F5C\u533A\u5E72\u51C0\uFF0C\u6CA1\u6709\u9700\u8981\u63D0\u4EA4\u7684\u66F4\u6539"));
1640
- return;
1641
- }
1642
- console.log(colors.yellow("\u6CA1\u6709\u6682\u5B58\u7684\u66F4\u6539"));
1643
- divider();
1644
- console.log("\u672A\u6682\u5B58\u7684\u6587\u4EF6:");
1645
- for (const { status, file } of unstaged) {
1646
- console.log(` ${formatFileStatus(status)} ${file}`);
1647
- }
1648
- divider();
1684
+ if (unstaged.length > 0) {
1649
1685
  const autoStage = config2.autoStage ?? true;
1650
1686
  if (autoStage) {
1651
1687
  execSync5("git add -A", { stdio: "pipe" });
@@ -1653,7 +1689,15 @@ async function commit() {
1653
1689
  divider();
1654
1690
  const newStatus = parseGitStatus();
1655
1691
  staged = newStatus.staged;
1656
- } else {
1692
+ unstaged = newStatus.unstaged;
1693
+ } else if (staged.length === 0) {
1694
+ console.log(colors.yellow("\u6CA1\u6709\u6682\u5B58\u7684\u66F4\u6539"));
1695
+ divider();
1696
+ console.log("\u672A\u6682\u5B58\u7684\u6587\u4EF6:");
1697
+ for (const { status, file } of unstaged) {
1698
+ console.log(` ${formatFileStatus(status)} ${file}`);
1699
+ }
1700
+ divider();
1657
1701
  const filesToStage = await checkbox({
1658
1702
  message: "\u9009\u62E9\u8981\u6682\u5B58\u7684\u6587\u4EF6:",
1659
1703
  choices: unstaged.map(({ status, file }) => ({
@@ -1675,13 +1719,16 @@ async function commit() {
1675
1719
  const newStatus = parseGitStatus();
1676
1720
  staged = newStatus.staged;
1677
1721
  }
1678
- } else {
1679
- console.log("\u5DF2\u6682\u5B58\u7684\u6587\u4EF6:");
1680
- for (const { status, file } of staged) {
1681
- console.log(` ${formatFileStatus(status)} ${file}`);
1682
- }
1683
- divider();
1684
1722
  }
1723
+ if (staged.length === 0) {
1724
+ console.log(colors.yellow("\u5DE5\u4F5C\u533A\u5E72\u51C0\uFF0C\u6CA1\u6709\u9700\u8981\u63D0\u4EA4\u7684\u66F4\u6539"));
1725
+ return;
1726
+ }
1727
+ console.log("\u5DF2\u6682\u5B58\u7684\u6587\u4EF6:");
1728
+ for (const { status, file } of staged) {
1729
+ console.log(` ${formatFileStatus(status)} ${file}`);
1730
+ }
1731
+ divider();
1685
1732
  const aiAvailable = isAICommitAvailable(config2);
1686
1733
  let commitMode = "manual";
1687
1734
  if (aiAvailable) {
@@ -1876,7 +1923,10 @@ Tag \u547D\u4EE4:
1876
1923
  gw r \u540C\u4E0A (\u522B\u540D)
1877
1924
 
1878
1925
  \u914D\u7F6E\u547D\u4EE4:
1879
- gw init \u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6 .gwrc.json
1926
+ gw init \u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6
1927
+ \u2022 \u5168\u5C40\u914D\u7F6E: ~/.gwrc.json (\u6240\u6709\u9879\u76EE\u751F\u6548)
1928
+ \u2022 \u9879\u76EE\u914D\u7F6E: .gwrc.json (\u4EC5\u5F53\u524D\u9879\u76EE)
1929
+ \u8FD0\u884C\u65F6\u53EF\u9009\u62E9\u914D\u7F6E\u8303\u56F4
1880
1930
 
1881
1931
  Stash \u547D\u4EE4:
1882
1932
  gw stash \u4EA4\u4E92\u5F0F\u7BA1\u7406 stash
@@ -1911,8 +1961,8 @@ Commit \u547D\u4EE4:
1911
1961
  // src/update-notifier.ts
1912
1962
  import { execSync as execSync6 } from "child_process";
1913
1963
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
1914
- import { homedir } from "os";
1915
- import { join as join2 } from "path";
1964
+ import { homedir as homedir3 } from "os";
1965
+ import { join as join3 } from "path";
1916
1966
  import boxen from "boxen";
1917
1967
  import { select as select7 } from "@inquirer/prompts";
1918
1968
  import ora5 from "ora";
@@ -2007,15 +2057,7 @@ async function performUpdate(packageName) {
2007
2057
  spinner: "dots"
2008
2058
  }).start();
2009
2059
  try {
2010
- try {
2011
- execSync6(`npm uninstall -g ${packageName}`, {
2012
- encoding: "utf-8",
2013
- stdio: ["pipe", "pipe", "pipe"]
2014
- });
2015
- spinner.text = "\u6B63\u5728\u5B89\u88C5\u65B0\u7248\u672C...";
2016
- } catch {
2017
- }
2018
- execSync6(`npm install -g ${packageName}`, {
2060
+ execSync6(`npm install -g ${packageName}@latest`, {
2019
2061
  encoding: "utf-8",
2020
2062
  stdio: ["pipe", "pipe", "pipe"]
2021
2063
  });
@@ -2026,11 +2068,7 @@ async function performUpdate(packageName) {
2026
2068
  [
2027
2069
  colors.bold("\u2728 \u66F4\u65B0\u5B8C\u6210\uFF01"),
2028
2070
  "",
2029
- colors.dim("\u8BF7\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u5237\u65B0\u5E76\u4F7F\u7528\u65B0\u7248\u672C:"),
2030
- "",
2031
- colors.yellow(" hash -r && gw --version"),
2032
- "",
2033
- colors.dim("\u6216\u8005\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF")
2071
+ colors.dim("\u8BF7\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u4F7F\u7528\u65B0\u7248\u672C")
2034
2072
  ].join("\n"),
2035
2073
  {
2036
2074
  padding: 1,
@@ -2046,13 +2084,13 @@ async function performUpdate(packageName) {
2046
2084
  spinner.fail(colors.red("\u66F4\u65B0\u5931\u8D25"));
2047
2085
  console.log("");
2048
2086
  console.log(colors.dim(" \u4F60\u53EF\u4EE5\u624B\u52A8\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0:"));
2049
- console.log(colors.cyan(` npm install -g ${packageName}`));
2087
+ console.log(colors.cyan(` npm install -g ${packageName}@latest`));
2050
2088
  console.log("");
2051
2089
  }
2052
2090
  }
2053
2091
  function readCache() {
2054
2092
  try {
2055
- const cacheFile = join2(homedir(), CACHE_FILE);
2093
+ const cacheFile = join3(homedir3(), CACHE_FILE);
2056
2094
  if (!existsSync3(cacheFile)) {
2057
2095
  return null;
2058
2096
  }
@@ -2064,7 +2102,7 @@ function readCache() {
2064
2102
  }
2065
2103
  function writeCache(cache) {
2066
2104
  try {
2067
- const cacheFile = join2(homedir(), CACHE_FILE);
2105
+ const cacheFile = join3(homedir3(), CACHE_FILE);
2068
2106
  writeFileSync3(cacheFile, JSON.stringify(cache), "utf-8");
2069
2107
  } catch {
2070
2108
  }
@@ -2095,7 +2133,7 @@ process.on("SIGTERM", () => {
2095
2133
  console.log("");
2096
2134
  process.exit(0);
2097
2135
  });
2098
- var version = true ? "0.2.16" : "0.0.0-dev";
2136
+ var version = true ? "0.2.18" : "0.0.0-dev";
2099
2137
  async function mainMenu() {
2100
2138
  console.log(
2101
2139
  colors.green(`
@@ -2107,7 +2145,7 @@ async function mainMenu() {
2107
2145
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
2108
2146
  `)
2109
2147
  );
2110
- console.log(colors.dim(` git-workflow v${version}
2148
+ console.log(colors.dim(` git-workflow v${colors.yellow(version)}
2111
2149
  `));
2112
2150
  const action = await select8({
2113
2151
  message: "\u9009\u62E9\u64CD\u4F5C:",
@@ -2275,5 +2313,10 @@ cli.help((sections) => {
2275
2313
  body: showHelp()
2276
2314
  });
2277
2315
  });
2278
- cli.version(version);
2316
+ cli.option("-v, --version", "\u663E\u793A\u7248\u672C\u53F7");
2317
+ var args = process.argv.slice(2);
2318
+ if (args.includes("-v") || args.includes("--version")) {
2319
+ console.log(colors.yellow(`v${version}`));
2320
+ process.exit(0);
2321
+ }
2279
2322
  cli.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zjex/git-workflow",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "🚀 极简的 Git 工作流 CLI 工具,让分支管理和版本发布变得轻松愉快",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,12 +29,15 @@ function exec(command, silent = false) {
29
29
  }
30
30
  }
31
31
 
32
- function runStep(stepNum, stepName, command) {
32
+ async function runStep(stepNum, stepName, command) {
33
33
  const spinner = ora({
34
34
  text: `${colors.blue(`[${stepNum}/${TOTAL_STEPS}]`)} ${stepName}...`,
35
35
  spinner: "dots",
36
36
  }).start();
37
37
 
38
+ // 给 spinner 一点时间渲染
39
+ await new Promise((resolve) => setTimeout(resolve, 100));
40
+
38
41
  try {
39
42
  execSync(command, { encoding: "utf-8", stdio: "pipe" });
40
43
  spinner.succeed(
@@ -56,7 +59,7 @@ async function main() {
56
59
  console.log("");
57
60
 
58
61
  // [1] 检查 Git 仓库
59
- if (!runStep(1, "检查 Git 仓库", "git rev-parse --git-dir")) {
62
+ if (!(await runStep(1, "检查 Git 仓库", "git rev-parse --git-dir"))) {
60
63
  console.log(colors.red("✖ 当前目录不是 git 仓库"));
61
64
  process.exit(1);
62
65
  }
@@ -67,6 +70,8 @@ async function main() {
67
70
  spinner: "dots",
68
71
  }).start();
69
72
 
73
+ await new Promise((resolve) => setTimeout(resolve, 100));
74
+
70
75
  const status = exec("git status --porcelain", true);
71
76
  if (status && status.trim()) {
72
77
  spinner2.fail(`${colors.blue("[2/11]")} 检查工作区状态`);
@@ -86,6 +91,8 @@ async function main() {
86
91
  spinner: "dots",
87
92
  }).start();
88
93
 
94
+ await new Promise((resolve) => setTimeout(resolve, 100));
95
+
89
96
  const npmUser = exec("npm whoami", true);
90
97
  if (!npmUser) {
91
98
  spinner3.fail(`${colors.blue("[3/11]")} 检查 npm 登录状态`);
@@ -102,7 +109,7 @@ async function main() {
102
109
  const currentBranch = exec("git branch --show-current", true).trim();
103
110
 
104
111
  // [4] 拉取最新代码
105
- if (!runStep(4, "拉取最新代码", `git pull origin ${currentBranch}`)) {
112
+ if (!(await runStep(4, "拉取最新代码", `git pull origin ${currentBranch}`))) {
106
113
  process.exit(1);
107
114
  }
108
115
 
@@ -167,12 +174,12 @@ async function main() {
167
174
  );
168
175
 
169
176
  // [6] 构建项目
170
- if (!runStep(6, "构建项目", "npm run build")) {
177
+ if (!(await runStep(6, "构建项目", "npm run build"))) {
171
178
  process.exit(1);
172
179
  }
173
180
 
174
181
  // [7] 生成 CHANGELOG
175
- if (!runStep(7, "生成 CHANGELOG", "npm run changelog")) {
182
+ if (!(await runStep(7, "生成 CHANGELOG", "npm run changelog"))) {
176
183
  process.exit(1);
177
184
  }
178
185
 
@@ -182,6 +189,8 @@ async function main() {
182
189
  spinner: "dots",
183
190
  }).start();
184
191
 
192
+ await new Promise((resolve) => setTimeout(resolve, 100));
193
+
185
194
  try {
186
195
  execSync("git add package.json CHANGELOG.md", { stdio: "pipe" });
187
196
  execSync(`git commit -m "🔖 chore(release): 发布 v${newVersion}"`, {
@@ -206,6 +215,8 @@ async function main() {
206
215
  spinner: "dots",
207
216
  }).start();
208
217
 
218
+ await new Promise((resolve) => setTimeout(resolve, 100));
219
+
209
220
  try {
210
221
  execSync(`git tag -a "v${newVersion}" -m "Release v${newVersion}"`, {
211
222
  stdio: "pipe",
@@ -227,6 +238,8 @@ async function main() {
227
238
  spinner: "dots",
228
239
  }).start();
229
240
 
241
+ await new Promise((resolve) => setTimeout(resolve, 100));
242
+
230
243
  try {
231
244
  execSync(`git push origin ${currentBranch}`, { stdio: "pipe" });
232
245
  execSync(`git push origin v${newVersion}`, { stdio: "pipe" });