@zjex/git-workflow 0.0.1 → 0.0.2

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.
@@ -0,0 +1,263 @@
1
+ import { execSync } from "child_process";
2
+ import { select, input, confirm, checkbox } from "@inquirer/prompts";
3
+ import ora from "ora";
4
+ import { colors, theme, execOutput, divider } from "../utils.js";
5
+ import { getConfig } from "../config.js";
6
+
7
+ // Conventional Commits 类型 + Gitmoji
8
+ const DEFAULT_COMMIT_TYPES = [
9
+ { type: "feat", emoji: "✨", description: "新功能" },
10
+ { type: "fix", emoji: "🐛", description: "修复 Bug" },
11
+ { type: "docs", emoji: "📝", description: "文档更新" },
12
+ { type: "style", emoji: "💄", description: "代码格式 (不影响功能)" },
13
+ { type: "refactor", emoji: "♻️", description: "重构 (非新功能/修复)" },
14
+ { type: "perf", emoji: "⚡️", description: "性能优化" },
15
+ { type: "test", emoji: "✅", description: "测试相关" },
16
+ { type: "build", emoji: "📦", description: "构建/依赖相关" },
17
+ { type: "ci", emoji: "👷", description: "CI/CD 相关" },
18
+ { type: "chore", emoji: "🔧", description: "其他杂项" },
19
+ { type: "revert", emoji: "⏪", description: "回退提交" },
20
+ ] as const;
21
+
22
+ type CommitType = (typeof DEFAULT_COMMIT_TYPES)[number]["type"];
23
+
24
+ function getCommitTypes(config: ReturnType<typeof getConfig>) {
25
+ const customEmojis = config.commitEmojis || {};
26
+ return DEFAULT_COMMIT_TYPES.map((item) => ({
27
+ ...item,
28
+ emoji: customEmojis[item.type as CommitType] || item.emoji,
29
+ }));
30
+ }
31
+
32
+ interface FileStatus {
33
+ status: string;
34
+ file: string;
35
+ }
36
+
37
+ function parseGitStatus(): { staged: FileStatus[]; unstaged: FileStatus[] } {
38
+ const output = execOutput("git status --porcelain");
39
+ if (!output) return { staged: [], unstaged: [] };
40
+
41
+ const staged: FileStatus[] = [];
42
+ const unstaged: FileStatus[] = [];
43
+
44
+ for (const line of output.split("\n")) {
45
+ if (!line) continue;
46
+ const indexStatus = line[0];
47
+ const workTreeStatus = line[1];
48
+ const file = line.slice(3);
49
+
50
+ // 已暂存的更改 (index 有状态)
51
+ if (indexStatus !== " " && indexStatus !== "?") {
52
+ staged.push({ status: indexStatus, file });
53
+ }
54
+
55
+ // 未暂存的更改 (work tree 有状态,或者是未跟踪文件)
56
+ if (workTreeStatus !== " " || indexStatus === "?") {
57
+ const status = indexStatus === "?" ? "?" : workTreeStatus;
58
+ unstaged.push({ status, file });
59
+ }
60
+ }
61
+
62
+ return { staged, unstaged };
63
+ }
64
+
65
+ function formatFileStatus(status: string): string {
66
+ const statusMap: Record<string, string> = {
67
+ M: colors.yellow("M"),
68
+ A: colors.green("A"),
69
+ D: colors.red("D"),
70
+ R: colors.yellow("R"),
71
+ C: colors.yellow("C"),
72
+ "?": colors.green("?"),
73
+ };
74
+ return statusMap[status] || status;
75
+ }
76
+
77
+ export async function commit(): Promise<void> {
78
+ const config = getConfig();
79
+ let { staged, unstaged } = parseGitStatus();
80
+
81
+ // 没有暂存的更改
82
+ if (staged.length === 0) {
83
+ if (unstaged.length === 0) {
84
+ console.log(colors.yellow("工作区干净,没有需要提交的更改"));
85
+ return;
86
+ }
87
+
88
+ console.log(colors.yellow("没有暂存的更改"));
89
+ divider();
90
+ console.log("未暂存的文件:");
91
+ for (const { status, file } of unstaged) {
92
+ console.log(` ${formatFileStatus(status)} ${file}`);
93
+ }
94
+ divider();
95
+
96
+ // 根据配置决定是否自动暂存
97
+ const autoStage = config.autoStage ?? true;
98
+
99
+ if (autoStage) {
100
+ // 自动暂存所有文件
101
+ execSync("git add -A", { stdio: "pipe" });
102
+ console.log(colors.green("✔ 已自动暂存所有更改"));
103
+ divider();
104
+ // 重新获取状态
105
+ const newStatus = parseGitStatus();
106
+ staged = newStatus.staged;
107
+ } else {
108
+ // 让用户选择要暂存的文件
109
+ const filesToStage = await checkbox({
110
+ message: "选择要暂存的文件:",
111
+ choices: unstaged.map(({ status, file }) => ({
112
+ name: `${formatFileStatus(status)} ${file}`,
113
+ value: file,
114
+ checked: true,
115
+ })),
116
+ theme,
117
+ });
118
+
119
+ if (filesToStage.length === 0) {
120
+ console.log(colors.yellow("没有选择任何文件,已取消"));
121
+ return;
122
+ }
123
+
124
+ // 暂存选中的文件
125
+ for (const file of filesToStage) {
126
+ execSync(`git add "${file}"`, { stdio: "pipe" });
127
+ }
128
+ console.log(colors.green(`✔ 已暂存 ${filesToStage.length} 个文件`));
129
+ divider();
130
+
131
+ // 重新获取状态
132
+ const newStatus = parseGitStatus();
133
+ staged = newStatus.staged;
134
+ }
135
+ } else {
136
+ console.log("已暂存的文件:");
137
+ for (const { status, file } of staged) {
138
+ console.log(` ${formatFileStatus(status)} ${file}`);
139
+ }
140
+ divider();
141
+ }
142
+
143
+ // 获取提交类型(支持自定义 emoji)
144
+ const commitTypes = getCommitTypes(config);
145
+
146
+ // 选择提交类型
147
+ const typeChoice = await select({
148
+ message: "选择提交类型:",
149
+ choices: commitTypes.map((t) => ({
150
+ name: `${t.emoji} ${t.type.padEnd(10)} ${colors.dim(t.description)}`,
151
+ value: t,
152
+ })),
153
+ theme,
154
+ });
155
+
156
+ // 输入 scope (可选)
157
+ const scope = await input({
158
+ message: "输入影响范围 scope (可跳过):",
159
+ theme,
160
+ });
161
+
162
+ // 输入简短描述
163
+ const subject = await input({
164
+ message: "输入简短描述:",
165
+ validate: (value) => {
166
+ if (!value.trim()) return "描述不能为空";
167
+ if (value.length > 72) return "描述不能超过 72 个字符";
168
+ return true;
169
+ },
170
+ theme,
171
+ });
172
+
173
+ // 输入详细描述 (可选)
174
+ const body = await input({
175
+ message: "输入详细描述 (可跳过):",
176
+ theme,
177
+ });
178
+
179
+ // 是否有破坏性变更
180
+ const hasBreaking = await confirm({
181
+ message: "是否包含破坏性变更 (BREAKING CHANGE)?",
182
+ default: false,
183
+ theme,
184
+ });
185
+
186
+ let breakingDesc = "";
187
+ if (hasBreaking) {
188
+ breakingDesc = await input({
189
+ message: "描述破坏性变更:",
190
+ validate: (value) => (value.trim() ? true : "请描述破坏性变更"),
191
+ theme,
192
+ });
193
+ }
194
+
195
+ // 关联 Issue (可选)
196
+ const issues = await input({
197
+ message: "关联 Issue (如 #123, 可跳过):",
198
+ theme,
199
+ });
200
+
201
+ // 构建 commit message
202
+ const { type, emoji } = typeChoice;
203
+ const scopePart = scope ? `(${scope})` : "";
204
+ const breakingMark = hasBreaking ? "!" : "";
205
+
206
+ // 根据配置决定是否使用 emoji
207
+ const useEmoji = config.useEmoji ?? true;
208
+ const emojiPrefix = useEmoji ? `${emoji} ` : "";
209
+
210
+ // Header: [emoji] type(scope)!: subject
211
+ let message = `${emojiPrefix}${type}${scopePart}${breakingMark}: ${subject}`;
212
+
213
+ // Body
214
+ if (body || hasBreaking || issues) {
215
+ message += "\n";
216
+
217
+ if (body) {
218
+ message += `\n${body}`;
219
+ }
220
+
221
+ if (hasBreaking) {
222
+ message += `\n\nBREAKING CHANGE: ${breakingDesc}`;
223
+ }
224
+
225
+ if (issues) {
226
+ message += `\n\n${issues}`;
227
+ }
228
+ }
229
+
230
+ divider();
231
+ console.log("提交信息预览:");
232
+ console.log(colors.green(message));
233
+ divider();
234
+
235
+ const shouldCommit = await confirm({
236
+ message: "确认提交?",
237
+ default: true,
238
+ theme,
239
+ });
240
+
241
+ if (!shouldCommit) {
242
+ console.log(colors.yellow("已取消"));
243
+ return;
244
+ }
245
+
246
+ const spinner = ora("正在提交...").start();
247
+
248
+ try {
249
+ // 使用 -m 参数,需要转义引号
250
+ const escapedMessage = message.replace(/"/g, '\\"');
251
+ execSync(`git commit -m "${escapedMessage}"`, { stdio: "pipe" });
252
+ spinner.succeed("提交成功");
253
+
254
+ // 显示提交信息
255
+ const commitHash = execOutput("git rev-parse --short HEAD");
256
+ console.log(colors.dim(`commit: ${commitHash}`));
257
+ } catch (error) {
258
+ spinner.fail("提交失败");
259
+ if (error instanceof Error) {
260
+ console.log(colors.red(error.message));
261
+ }
262
+ }
263
+ }
@@ -4,43 +4,52 @@ export function showHelp(): string {
4
4
  return `
5
5
  分支命令:
6
6
  gw feature [--base <branch>] 创建 feature 分支
7
- gw feat [--base <branch>] 同上 (简写)
8
- gw f [--base <branch>] 同上 (简写)
7
+ gw feat [--base <branch>] 同上 (别名)
8
+ gw f [--base <branch>] 同上 (别名)
9
9
 
10
10
  gw hotfix [--base <branch>] 创建 hotfix 分支
11
- gw fix [--base <branch>] 同上 (简写)
12
- gw h [--base <branch>] 同上 (简写)
11
+ gw fix [--base <branch>] 同上 (别名)
12
+ gw h [--base <branch>] 同上 (别名)
13
13
 
14
14
  gw delete [branch] 删除本地/远程分支
15
- gw del [branch] 同上 (简写)
16
- gw d [branch] 同上 (简写)
15
+ gw del [branch] 同上 (别名)
16
+ gw d [branch] 同上 (别名)
17
17
 
18
18
  Tag 命令:
19
19
  gw tags [prefix] 列出所有 tag,可按前缀过滤
20
- gw ts [prefix] 同上 (简写)
20
+ gw ts [prefix] 同上 (别名)
21
21
 
22
22
  gw tag [prefix] 交互式选择版本类型并创建 tag
23
- gw t [prefix] 同上 (简写)
23
+ gw t [prefix] 同上 (别名)
24
24
 
25
25
  发布命令:
26
26
  gw release 交互式选择版本号并更新 package.json
27
- gw r 同上 (简写)
27
+ gw r 同上 (别名)
28
28
 
29
29
  配置命令:
30
30
  gw init 初始化配置文件 .gwrc.json
31
31
 
32
32
  Stash 命令:
33
33
  gw stash 交互式管理 stash
34
- gw s 同上 (简写)
34
+ gw s 同上 (别名)
35
+ gw st 同上 (别名)
36
+
37
+ Commit 命令:
38
+ gw commit 交互式提交 (Conventional Commits + Gitmoji)
39
+ gw c 同上 (别名)
40
+ gw cm 同上 (别名)
35
41
 
36
42
  示例:
37
- gw feat 基于 main/master 创建 feature 分支
38
- gw feat --base develop 基于 develop 分支创建 feature 分支
39
- gw fix --base release 基于 release 分支创建 hotfix 分支
40
- gw del 交互式选择并删除分支
41
- gw del feature/xxx 直接删除指定分支
42
- gw tags v 列出所有 v 开头的 tag
43
- gw tag 交互式创建 tag
43
+ gw f 基于 main/master 创建 feature 分支
44
+ gw f --base develop 基于 develop 分支创建 feature 分支
45
+ gw h --base release 基于 release 分支创建 hotfix 分支
46
+ gw d 交互式选择并删除分支
47
+ gw d feature/xxx 直接删除指定分支
48
+ gw ts v 列出所有 v 开头的 tag
49
+ gw t 交互式创建 tag
50
+ gw r 交互式发布版本
51
+ gw s 交互式管理 stash
52
+ gw c 交互式提交代码
44
53
 
45
54
  分支命名格式:
46
55
  feature/${TODAY}-<Story ID>-<描述>
@@ -5,6 +5,21 @@ import type { GwConfig } from "../config.js";
5
5
 
6
6
  const CONFIG_FILE = ".gwrc.json";
7
7
 
8
+ // 默认的 commit emoji 配置
9
+ const DEFAULT_COMMIT_EMOJIS = {
10
+ feat: "✨",
11
+ fix: "🐛",
12
+ docs: "📝",
13
+ style: "💄",
14
+ refactor: "♻️",
15
+ perf: "⚡️",
16
+ test: "✅",
17
+ build: "📦",
18
+ ci: "👷",
19
+ chore: "🔧",
20
+ revert: "⏪",
21
+ };
22
+
8
23
  export async function init(): Promise<void> {
9
24
  if (existsSync(CONFIG_FILE)) {
10
25
  const overwrite = await confirm({
@@ -94,10 +109,35 @@ export async function init(): Promise<void> {
94
109
 
95
110
  divider();
96
111
 
112
+ // Commit 配置
113
+ const autoStage = await confirm({
114
+ message: "Commit 时是否自动暂存所有更改?",
115
+ default: true,
116
+ theme,
117
+ });
118
+ if (!autoStage) config.autoStage = false;
119
+
120
+ const useEmoji = await confirm({
121
+ message: "Commit 时是否使用 emoji?",
122
+ default: true,
123
+ theme,
124
+ });
125
+ if (!useEmoji) config.useEmoji = false;
126
+
127
+ // 始终写入默认的 commitEmojis 配置,方便用户修改
128
+ config.commitEmojis = DEFAULT_COMMIT_EMOJIS;
129
+
130
+ divider();
131
+
97
132
  // 写入配置
98
133
  const content = JSON.stringify(config, null, 2);
99
134
  writeFileSync(CONFIG_FILE, content + "\n");
100
135
 
101
136
  console.log(colors.green(`✓ 配置已保存到 ${CONFIG_FILE}`));
137
+ console.log(
138
+ colors.dim(
139
+ "\n提示: 可以在配置文件中修改 commitEmojis 来自定义各类型的 emoji"
140
+ )
141
+ );
102
142
  console.log(colors.dim("\n" + content));
103
143
  }
@@ -42,6 +42,14 @@ interface TagChoice {
42
42
  value: string;
43
43
  }
44
44
 
45
+ // 获取指定前缀的最新 tag(不依赖 shell 管道)
46
+ function getLatestTag(prefix: string): string {
47
+ const tags = execOutput(`git tag -l "${prefix}*" --sort=-v:refname`)
48
+ .split("\n")
49
+ .filter(Boolean);
50
+ return tags[0] || "";
51
+ }
52
+
45
53
  export async function createTag(inputPrefix?: string): Promise<void> {
46
54
  const config = getConfig();
47
55
  const fetchSpinner = ora("正在获取 tags...").start();
@@ -60,13 +68,68 @@ export async function createTag(inputPrefix?: string): Promise<void> {
60
68
 
61
69
  if (!prefix) {
62
70
  const allTags = execOutput("git tag -l").split("\n").filter(Boolean);
71
+
72
+ // 仓库没有任何 tag 的情况
73
+ if (allTags.length === 0) {
74
+ prefix = await input({
75
+ message: "当前仓库没有 tag,请输入前缀 (如 v):",
76
+ default: "v",
77
+ theme,
78
+ });
79
+ if (!prefix) {
80
+ console.log(colors.yellow("已取消"));
81
+ return;
82
+ }
83
+
84
+ // 选择初始版本号
85
+ const initialVersion = await select({
86
+ message: "选择初始版本号:",
87
+ choices: [
88
+ { name: `${prefix}0.0.1`, value: "0.0.1" },
89
+ { name: `${prefix}0.1.0`, value: "0.1.0" },
90
+ { name: `${prefix}1.0.0`, value: "1.0.0" },
91
+ { name: "自定义...", value: "__custom__" },
92
+ ],
93
+ theme,
94
+ });
95
+
96
+ let version = initialVersion;
97
+ if (initialVersion === "__custom__") {
98
+ version = await input({
99
+ message: "请输入版本号 (如 0.0.1):",
100
+ theme,
101
+ });
102
+ if (!version) {
103
+ console.log(colors.yellow("已取消"));
104
+ return;
105
+ }
106
+ }
107
+
108
+ const newTag = `${prefix}${version}`;
109
+ const ok = await select({
110
+ message: `确认创建 ${newTag}?`,
111
+ choices: [
112
+ { name: "是", value: true },
113
+ { name: "否", value: false },
114
+ ],
115
+ theme,
116
+ });
117
+ if (ok) {
118
+ doCreateTag(newTag);
119
+ }
120
+ return;
121
+ }
122
+
123
+ // 从现有 tag 中提取前缀
63
124
  const prefixes = [
64
125
  ...new Set(allTags.map((t) => t.replace(/[0-9].*/, "")).filter(Boolean)),
65
126
  ];
66
127
 
67
128
  if (prefixes.length === 0) {
129
+ // 有 tag 但无法提取前缀(比如纯数字 tag)
68
130
  prefix = await input({
69
- message: "当前仓库没有 tag,请输入新前缀 (如 v):",
131
+ message: "请输入 tag 前缀 (如 v):",
132
+ default: "v",
70
133
  theme,
71
134
  });
72
135
  if (!prefix) {
@@ -75,9 +138,7 @@ export async function createTag(inputPrefix?: string): Promise<void> {
75
138
  }
76
139
  } else {
77
140
  const prefixWithDate: PrefixInfo[] = prefixes.map((p) => {
78
- const latest = execOutput(
79
- `git tag -l "${p}*" --sort=-v:refname | head -1`
80
- );
141
+ const latest = getLatestTag(p);
81
142
  const date = latest
82
143
  ? execOutput(`git log -1 --format=%ct "${latest}" 2>/dev/null`)
83
144
  : "0";
@@ -108,9 +169,7 @@ export async function createTag(inputPrefix?: string): Promise<void> {
108
169
  }
109
170
  }
110
171
 
111
- const latestTag = execOutput(
112
- `git tag -l "${prefix}*" --sort=-v:refname | head -1`
113
- );
172
+ const latestTag = getLatestTag(prefix);
114
173
 
115
174
  if (!latestTag) {
116
175
  const newTag = `${prefix}1.0.0`;
package/src/config.ts CHANGED
@@ -18,6 +18,24 @@ export interface GwConfig {
18
18
  defaultTagPrefix?: string;
19
19
  // 创建分支后是否自动推送,默认询问
20
20
  autoPush?: boolean;
21
+ // commit 时是否自动暂存所有更改,默认 true
22
+ autoStage?: boolean;
23
+ // commit 时是否使用 emoji,默认 true
24
+ useEmoji?: boolean;
25
+ // 自定义 commit 类型的 emoji
26
+ commitEmojis?: {
27
+ feat?: string;
28
+ fix?: string;
29
+ docs?: string;
30
+ style?: string;
31
+ refactor?: string;
32
+ perf?: string;
33
+ test?: string;
34
+ build?: string;
35
+ ci?: string;
36
+ chore?: string;
37
+ revert?: string;
38
+ };
21
39
  }
22
40
 
23
41
  const defaultConfig: GwConfig = {
@@ -26,6 +44,8 @@ const defaultConfig: GwConfig = {
26
44
  requireId: false,
27
45
  featureIdLabel: "Story ID",
28
46
  hotfixIdLabel: "Issue ID",
47
+ autoStage: true,
48
+ useEmoji: true,
29
49
  };
30
50
 
31
51
  function getGitRoot(): string {
package/src/index.ts CHANGED
@@ -1,18 +1,146 @@
1
1
  // @ts-nocheck shebang handled by tsup banner
2
2
 
3
3
  import { cac } from "cac";
4
- import { checkGitRepo } from "./utils.js";
4
+ import { select } from "@inquirer/prompts";
5
+ import { ExitPromptError } from "@inquirer/core";
6
+ import { checkGitRepo, theme, colors } from "./utils.js";
5
7
  import { createBranch, deleteBranch } from "./commands/branch.js";
6
8
  import { listTags, createTag } from "./commands/tag.js";
7
9
  import { release } from "./commands/release.js";
8
10
  import { init } from "./commands/init.js";
9
11
  import { stash } from "./commands/stash.js";
12
+ import { commit } from "./commands/commit.js";
10
13
  import { showHelp } from "./commands/help.js";
11
14
 
12
- declare const __VERSION__: string;
15
+ // 捕获 Ctrl+C 退出,静默处理
16
+ process.on("uncaughtException", (err) => {
17
+ if (err instanceof ExitPromptError) {
18
+ process.exit(0);
19
+ }
20
+ console.error(err);
21
+ process.exit(1);
22
+ });
23
+
24
+ declare const __VERSION__: string | undefined;
25
+
26
+ // 开发环境下从 package.json 读取版本号
27
+ const version: string =
28
+ typeof __VERSION__ !== "undefined"
29
+ ? __VERSION__
30
+ : (await import("../package.json", { with: { type: "json" } })).default
31
+ .version;
32
+
33
+ // 交互式主菜单
34
+ async function mainMenu(): Promise<void> {
35
+ // ASCII Art Logo
36
+ console.log(
37
+ colors.green(`
38
+ ███████╗ ██╗███████╗██╗ ██╗
39
+ ╚══███╔╝ ██║██╔════╝╚██╗██╔╝
40
+ ███╔╝ ██║█████╗ ╚███╔╝
41
+ ███╔╝ ██ ██║██╔══╝ ██╔██╗
42
+ ███████╗╚█████╔╝███████╗██╔╝ ██╗
43
+ ╚══════╝ ╚════╝ ╚══════╝╚═╝ ╚═╝
44
+ `)
45
+ );
46
+ console.log(colors.dim(` git-workflow v${version}\n`));
47
+
48
+ const action = await select({
49
+ message: "选择操作:",
50
+ choices: [
51
+ {
52
+ name: `[1] ✨ 创建 feature 分支 ${colors.dim("gw f")}`,
53
+ value: "feature",
54
+ },
55
+ {
56
+ name: `[2] 🐛 创建 hotfix 分支 ${colors.dim("gw h")}`,
57
+ value: "hotfix",
58
+ },
59
+ {
60
+ name: `[3] 🗑️ 删除分支 ${colors.dim("gw d")}`,
61
+ value: "delete",
62
+ },
63
+ {
64
+ name: `[4] 📝 提交代码 ${colors.dim("gw c")}`,
65
+ value: "commit",
66
+ },
67
+ {
68
+ name: `[5] 🏷️ 创建 tag ${colors.dim("gw t")}`,
69
+ value: "tag",
70
+ },
71
+ {
72
+ name: `[6] 📋 列出 tags ${colors.dim("gw ts")}`,
73
+ value: "tags",
74
+ },
75
+ {
76
+ name: `[7] 📦 发布版本 ${colors.dim("gw r")}`,
77
+ value: "release",
78
+ },
79
+ {
80
+ name: `[8] 💾 管理 stash ${colors.dim("gw s")}`,
81
+ value: "stash",
82
+ },
83
+ {
84
+ name: `[9] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
85
+ value: "init",
86
+ },
87
+ { name: "[0] ❓ 帮助", value: "help" },
88
+ { name: "[q] 退出", value: "exit" },
89
+ ],
90
+ loop: false,
91
+ theme,
92
+ });
93
+
94
+ switch (action) {
95
+ case "feature":
96
+ checkGitRepo();
97
+ await createBranch("feature");
98
+ break;
99
+ case "hotfix":
100
+ checkGitRepo();
101
+ await createBranch("hotfix");
102
+ break;
103
+ case "delete":
104
+ checkGitRepo();
105
+ await deleteBranch();
106
+ break;
107
+ case "tag":
108
+ checkGitRepo();
109
+ await createTag();
110
+ break;
111
+ case "tags":
112
+ checkGitRepo();
113
+ await listTags();
114
+ break;
115
+ case "commit":
116
+ checkGitRepo();
117
+ await commit();
118
+ break;
119
+ case "release":
120
+ await release();
121
+ break;
122
+ case "stash":
123
+ checkGitRepo();
124
+ await stash();
125
+ break;
126
+ case "init":
127
+ await init();
128
+ break;
129
+ case "help":
130
+ console.log(showHelp());
131
+ break;
132
+ case "exit":
133
+ break;
134
+ }
135
+ }
13
136
 
14
137
  const cli = cac("gw");
15
138
 
139
+ // 默认命令 - 显示交互式菜单
140
+ cli.command("", "显示交互式菜单").action(() => {
141
+ return mainMenu();
142
+ });
143
+
16
144
  cli
17
145
  .command("feature", "创建 feature 分支")
18
146
  .alias("feat")
@@ -78,11 +206,20 @@ cli
78
206
  return stash();
79
207
  });
80
208
 
209
+ cli
210
+ .command("commit", "交互式提交 (Conventional Commits + Gitmoji)")
211
+ .alias("c")
212
+ .alias("cm")
213
+ .action(() => {
214
+ checkGitRepo();
215
+ return commit();
216
+ });
217
+
81
218
  cli.help((sections) => {
82
219
  sections.push({
83
220
  body: showHelp(),
84
221
  });
85
222
  });
86
- cli.version(__VERSION__);
223
+ cli.version(version);
87
224
 
88
225
  cli.parse();