@zjex/git-workflow 0.3.0 → 0.3.3

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,503 @@
1
+ /**
2
+ * @zjex/git-workflow - Log 命令
3
+ *
4
+ * 提供GitHub风格的时间线日志查看功能
5
+ */
6
+
7
+ import { execSync } from "child_process";
8
+ import boxen from "boxen";
9
+ import { colors } from "../utils.js";
10
+ import { spawn } from "child_process";
11
+ import { createWriteStream } from "fs";
12
+ import { tmpdir } from "os";
13
+ import { join } from "path";
14
+
15
+ /**
16
+ * 日志显示选项
17
+ */
18
+ interface LogOptions {
19
+ author?: string;
20
+ since?: string;
21
+ until?: string;
22
+ grep?: string;
23
+ limit?: number;
24
+ all?: boolean;
25
+ interactive?: boolean;
26
+ }
27
+
28
+ /**
29
+ * 提交信息接口
30
+ */
31
+ interface CommitInfo {
32
+ hash: string;
33
+ shortHash: string;
34
+ subject: string;
35
+ author: string;
36
+ date: string;
37
+ relativeDate: string;
38
+ refs: string;
39
+ }
40
+
41
+ /**
42
+ * 解析Git log输出为结构化数据
43
+ */
44
+ function parseGitLog(output: string): CommitInfo[] {
45
+ const commits: CommitInfo[] = [];
46
+ const lines = output.trim().split('\n');
47
+
48
+ for (const line of lines) {
49
+ if (!line.trim()) continue;
50
+
51
+ // 使用分隔符解析
52
+ const parts = line.split('|');
53
+ if (parts.length >= 6) {
54
+ commits.push({
55
+ hash: parts[0],
56
+ shortHash: parts[1],
57
+ subject: parts[2],
58
+ author: parts[3],
59
+ date: parts[4],
60
+ relativeDate: parts[5],
61
+ refs: parts[6] || ''
62
+ });
63
+ }
64
+ }
65
+
66
+ return commits;
67
+ }
68
+
69
+ /**
70
+ * 获取提交类型图标
71
+ */
72
+ function getCommitTypeIcon(subject: string): string {
73
+ const lowerSubject = subject.toLowerCase();
74
+
75
+ if (lowerSubject.includes('feat') || lowerSubject.includes('feature')) return '✨';
76
+ if (lowerSubject.includes('fix') || lowerSubject.includes('bug')) return '🐛';
77
+ if (lowerSubject.includes('docs') || lowerSubject.includes('doc')) return '📚';
78
+ if (lowerSubject.includes('style')) return '💄';
79
+ if (lowerSubject.includes('refactor')) return '♻️';
80
+ if (lowerSubject.includes('test')) return '🧪';
81
+ if (lowerSubject.includes('chore')) return '🔧';
82
+ if (lowerSubject.includes('perf')) return '⚡';
83
+ if (lowerSubject.includes('ci')) return '👷';
84
+ if (lowerSubject.includes('build')) return '📦';
85
+ if (lowerSubject.includes('revert')) return '⏪';
86
+ if (lowerSubject.includes('merge')) return '🔀';
87
+ if (lowerSubject.includes('release') || lowerSubject.includes('version')) return '🔖';
88
+
89
+ return '📝';
90
+ }
91
+
92
+ /**
93
+ * 按日期分组提交
94
+ */
95
+ function groupCommitsByDate(commits: CommitInfo[]): Map<string, CommitInfo[]> {
96
+ const groups = new Map<string, CommitInfo[]>();
97
+
98
+ for (const commit of commits) {
99
+ const date = commit.date;
100
+ if (!groups.has(date)) {
101
+ groups.set(date, []);
102
+ }
103
+ groups.get(date)!.push(commit);
104
+ }
105
+
106
+ return groups;
107
+ }
108
+
109
+ /**
110
+ * 格式化相对时间为中文
111
+ */
112
+ function formatRelativeTime(relativeDate: string): string {
113
+ let result = relativeDate;
114
+
115
+ // 先替换英文单词为中文
116
+ const timeMap: { [key: string]: string } = {
117
+ 'second': '秒',
118
+ 'seconds': '秒',
119
+ 'minute': '分钟',
120
+ 'minutes': '分钟',
121
+ 'hour': '小时',
122
+ 'hours': '小时',
123
+ 'day': '天',
124
+ 'days': '天',
125
+ 'week': '周',
126
+ 'weeks': '周',
127
+ 'month': '个月',
128
+ 'months': '个月',
129
+ 'year': '年',
130
+ 'years': '年',
131
+ 'ago': '前'
132
+ };
133
+
134
+ for (const [en, zh] of Object.entries(timeMap)) {
135
+ result = result.replace(new RegExp(`\\b${en}\\b`, 'g'), zh);
136
+ }
137
+
138
+ // 去掉数字和单位之间的空格,以及单位和"前"之间的空格
139
+ // 例如:"22 分钟 前" -> "22分钟前"
140
+ result = result.replace(/(\d+)\s+(秒|分钟|小时|天|周|个月|年)\s+前/g, '$1$2前');
141
+
142
+ // 简化显示格式
143
+ const match = result.match(/(\d+)(分钟|小时|天|周|个月|年)前/);
144
+ if (match) {
145
+ const num = parseInt(match[1]);
146
+ const unit = match[2];
147
+
148
+ // 超过60分钟显示小时
149
+ if (unit === '分钟' && num >= 60) {
150
+ const hours = Math.floor(num / 60);
151
+ return `${hours}小时前`;
152
+ }
153
+
154
+ // 超过24小时显示天数
155
+ if (unit === '小时' && num >= 24) {
156
+ const days = Math.floor(num / 24);
157
+ return `${days}天前`;
158
+ }
159
+
160
+ // 超过7天显示周数
161
+ if (unit === '天' && num >= 7 && num < 30) {
162
+ const weeks = Math.floor(num / 7);
163
+ return `${weeks}周前`;
164
+ }
165
+
166
+ // 超过30天显示月数
167
+ if (unit === '天' && num >= 30) {
168
+ const months = Math.floor(num / 30);
169
+ return `${months}个月前`;
170
+ }
171
+
172
+ // 超过4周显示月数
173
+ if (unit === '周' && num >= 4) {
174
+ const months = Math.floor(num / 4);
175
+ return `${months}个月前`;
176
+ }
177
+
178
+ // 超过12个月显示年数
179
+ if (unit === '个月' && num >= 12) {
180
+ const years = Math.floor(num / 12);
181
+ return `${years}年前`;
182
+ }
183
+ }
184
+
185
+ return result;
186
+ }
187
+
188
+ /**
189
+ * 解析提交主题,分离标题和子任务
190
+ */
191
+ function parseCommitSubject(subject: string): { title: string; tasks: string[] } {
192
+ // 检查是否包含 " - " 分隔的子任务
193
+ if (subject.includes(' - ')) {
194
+ const parts = subject.split(' - ');
195
+ const title = parts[0].trim();
196
+ const tasks = parts.slice(1).map(task => task.trim()).filter(task => task.length > 0);
197
+ return { title, tasks };
198
+ }
199
+
200
+ return { title: subject, tasks: [] };
201
+ }
202
+
203
+ /**
204
+ * 检查是否支持颜色输出
205
+ */
206
+ function supportsColor(): boolean {
207
+ // 在交互式模式下强制启用颜色
208
+ return true;
209
+ }
210
+
211
+ /**
212
+ * 格式化GitHub风格的时间线显示
213
+ */
214
+ function formatTimelineStyle(commits: CommitInfo[]): string {
215
+ const groupedCommits = groupCommitsByDate(commits);
216
+ let output = '';
217
+
218
+ // 按日期倒序排列
219
+ const sortedDates = Array.from(groupedCommits.keys()).sort((a, b) =>
220
+ new Date(b).getTime() - new Date(a).getTime()
221
+ );
222
+
223
+ const useColors = supportsColor() || process.env.FORCE_COLOR;
224
+
225
+ for (let dateIndex = 0; dateIndex < sortedDates.length; dateIndex++) {
226
+ const date = sortedDates[dateIndex];
227
+ const dateCommits = groupedCommits.get(date)!;
228
+
229
+ // 日期标题 - 使用黄色突出显示
230
+ const dateTitle = `📅 Commits on ${date}`;
231
+ if (useColors) {
232
+ output += '\n' + colors.bold(colors.yellow(dateTitle)) + '\n\n';
233
+ } else {
234
+ output += '\n' + dateTitle + '\n\n';
235
+ }
236
+
237
+ // 该日期下的提交
238
+ for (let commitIndex = 0; commitIndex < dateCommits.length; commitIndex++) {
239
+ const commit = dateCommits[commitIndex];
240
+ const icon = getCommitTypeIcon(commit.subject);
241
+ const { title, tasks } = parseCommitSubject(commit.subject);
242
+
243
+ // 构建提交内容
244
+ const commitContent = [];
245
+
246
+ // 主标题 - 使用白色加粗
247
+ if (useColors) {
248
+ commitContent.push(`${icon} ${colors.bold(colors.white(title))}`);
249
+ } else {
250
+ commitContent.push(`${icon} ${title}`);
251
+ }
252
+
253
+ // 如果有子任务,添加子任务列表
254
+ if (tasks.length > 0) {
255
+ commitContent.push(''); // 空行分隔
256
+ tasks.forEach(task => {
257
+ if (useColors) {
258
+ commitContent.push(` ${colors.dim('–')} ${colors.dim(task)}`);
259
+ } else {
260
+ commitContent.push(` – ${task}`);
261
+ }
262
+ });
263
+ }
264
+
265
+ // 空行分隔
266
+ commitContent.push('');
267
+
268
+ // 作者和时间信息
269
+ if (useColors) {
270
+ commitContent.push(`${colors.dim('👤')} ${colors.blue(commit.author)} ${colors.dim('committed')} ${colors.green(formatRelativeTime(commit.relativeDate))}`);
271
+ // Hash信息 - 使用橙色
272
+ commitContent.push(`${colors.dim('🔗')} ${colors.orange('#' + commit.shortHash)}`);
273
+ // 如果有分支/标签信息 - 区分显示
274
+ if (commit.refs && commit.refs.trim()) {
275
+ const refs = commit.refs.trim();
276
+ // 解析并分别显示分支和标签
277
+ const refParts = refs.split(', ');
278
+ const branches: string[] = [];
279
+ const tags: string[] = [];
280
+
281
+ refParts.forEach(ref => {
282
+ if (ref.startsWith('tag: ')) {
283
+ tags.push(ref.replace('tag: ', ''));
284
+ } else if (ref.includes('/') || ref === 'HEAD') {
285
+ branches.push(ref);
286
+ } else {
287
+ branches.push(ref);
288
+ }
289
+ });
290
+
291
+ // 显示分支信息
292
+ if (branches.length > 0) {
293
+ commitContent.push(`${colors.dim('🌿')} ${colors.lightPurple(branches.join(', '))}`);
294
+ }
295
+
296
+ // 显示标签信息
297
+ if (tags.length > 0) {
298
+ const tagText = tags.map(tag => `tag ${tag}`).join(', ');
299
+ commitContent.push(`${colors.dim('🔖')} ${colors.yellow(tagText)}`);
300
+ }
301
+ }
302
+ } else {
303
+ commitContent.push(`👤 ${commit.author} committed ${formatRelativeTime(commit.relativeDate)}`);
304
+ commitContent.push(`🔗 #${commit.shortHash}`);
305
+ if (commit.refs && commit.refs.trim()) {
306
+ const refs = commit.refs.trim();
307
+ // 解析并分别显示分支和标签
308
+ const refParts = refs.split(', ');
309
+ const branches: string[] = [];
310
+ const tags: string[] = [];
311
+
312
+ refParts.forEach(ref => {
313
+ if (ref.startsWith('tag: ')) {
314
+ tags.push(ref.replace('tag: ', ''));
315
+ } else if (ref.includes('/') || ref === 'HEAD') {
316
+ branches.push(ref);
317
+ } else {
318
+ branches.push(ref);
319
+ }
320
+ });
321
+
322
+ // 显示分支信息
323
+ if (branches.length > 0) {
324
+ commitContent.push(`🌿 ${branches.join(', ')}`);
325
+ }
326
+
327
+ // 显示标签信息
328
+ if (tags.length > 0) {
329
+ const tagText = tags.map(tag => `tag ${tag}`).join(', ');
330
+ commitContent.push(`🔖 ${tagText}`);
331
+ }
332
+ }
333
+ }
334
+
335
+ // 使用boxen
336
+ const commitBox = boxen(commitContent.join('\n'), {
337
+ padding: { top: 0, bottom: 0, left: 1, right: 1 },
338
+ margin: { top: 0, bottom: 1, left: 0, right: 0 },
339
+ borderStyle: 'round',
340
+ borderColor: 'gray'
341
+ });
342
+
343
+ output += commitBox + '\n';
344
+ }
345
+ }
346
+
347
+ return output;
348
+ }
349
+
350
+ /**
351
+ * 启动交互式分页查看器
352
+ */
353
+ function startInteractivePager(content: string): void {
354
+ // 使用系统的 less 命令作为分页器,启用颜色支持
355
+ const pager = process.env.PAGER || 'less';
356
+
357
+ try {
358
+ // -R: 支持ANSI颜色代码
359
+ // -S: 不换行长行
360
+ // -F: 如果内容少于一屏则直接退出
361
+ // -X: 不清屏
362
+ // -i: 忽略大小写搜索
363
+ const pagerProcess = spawn(pager, ['-R', '-S', '-F', '-X', '-i'], {
364
+ stdio: ['pipe', 'inherit', 'inherit'],
365
+ env: { ...process.env, LESS: '-R -S -F -X -i' }
366
+ });
367
+
368
+ // 将内容写入分页器
369
+ pagerProcess.stdin.write(content);
370
+ pagerProcess.stdin.end();
371
+
372
+ // 处理分页器退出
373
+ pagerProcess.on('exit', () => {
374
+ // 分页器退出后不需要额外处理
375
+ });
376
+
377
+ // 处理错误
378
+ pagerProcess.on('error', (err) => {
379
+ // 如果分页器启动失败,直接输出内容
380
+ console.log(content);
381
+ });
382
+
383
+ } catch (error) {
384
+ // 如果出错,直接输出内容
385
+ console.log(content);
386
+ }
387
+ }
388
+
389
+ /**
390
+ * 执行Git log并显示时间线风格结果
391
+ */
392
+ function executeTimelineLog(options: LogOptions): void {
393
+ try {
394
+ // 构建Git命令
395
+ let cmd = 'git log --pretty=format:"%H|%h|%s|%an|%ad|%ar|%D" --date=short';
396
+
397
+ // 添加选项
398
+ if (options.limit && !options.interactive) cmd += ` -${options.limit}`;
399
+ if (options.author) cmd += ` --author="${options.author}"`;
400
+ if (options.since) cmd += ` --since="${options.since}"`;
401
+ if (options.until) cmd += ` --until="${options.until}"`;
402
+ if (options.grep) cmd += ` --grep="${options.grep}"`;
403
+ if (options.all) cmd += ` --all`;
404
+
405
+ // 交互式模式默认显示更多提交
406
+ if (options.interactive && !options.limit) {
407
+ cmd += ` -50`; // 默认显示50个提交
408
+ }
409
+
410
+ const output = execSync(cmd, {
411
+ encoding: 'utf8',
412
+ stdio: 'pipe',
413
+ maxBuffer: 1024 * 1024 * 10
414
+ });
415
+
416
+ if (output.trim()) {
417
+ const commits = parseGitLog(output);
418
+
419
+ // 构建完整输出
420
+ let fullOutput = '';
421
+
422
+ // 显示标题
423
+ const title = `📊 共显示 ${commits.length} 个提交`;
424
+ fullOutput += '\n' + boxen(title, {
425
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
426
+ margin: { top: 0, bottom: 1, left: 0, right: 0 },
427
+ borderStyle: 'double',
428
+ borderColor: 'green',
429
+ textAlignment: 'center'
430
+ }) + '\n';
431
+
432
+ // 显示时间线
433
+ const timelineOutput = formatTimelineStyle(commits);
434
+ fullOutput += timelineOutput;
435
+
436
+ // 根据是否交互式模式选择输出方式
437
+ if (options.interactive) {
438
+ startInteractivePager(fullOutput);
439
+ } else {
440
+ console.log(fullOutput);
441
+ }
442
+
443
+ } else {
444
+ const noCommitsMsg = '\n' + boxen('📭 没有找到匹配的提交记录', {
445
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
446
+ borderStyle: 'round',
447
+ borderColor: 'yellow',
448
+ textAlignment: 'center'
449
+ });
450
+
451
+ if (options.interactive) {
452
+ startInteractivePager(noCommitsMsg);
453
+ } else {
454
+ console.log(noCommitsMsg);
455
+ }
456
+ }
457
+ } catch (error: any) {
458
+ let errorMessage = '❌ 执行失败';
459
+ if (error.status === 128) {
460
+ errorMessage = '❌ Git仓库错误或没有提交记录';
461
+ } else {
462
+ errorMessage = `❌ 执行失败: ${error.message}`;
463
+ }
464
+
465
+ const errorBox = '\n' + boxen(errorMessage, {
466
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
467
+ borderStyle: 'round',
468
+ borderColor: 'red',
469
+ textAlignment: 'center'
470
+ });
471
+
472
+ if (options.interactive) {
473
+ startInteractivePager(errorBox);
474
+ } else {
475
+ console.log(errorBox);
476
+ }
477
+ }
478
+ }
479
+
480
+ /**
481
+ * 主要的log命令函数
482
+ */
483
+ export async function log(options: LogOptions = {}): Promise<void> {
484
+ // 默认启用交互式模式
485
+ if (options.interactive === undefined) {
486
+ options.interactive = true;
487
+ }
488
+
489
+ // 交互式模式下不设置默认limit
490
+ if (!options.interactive && !options.limit) {
491
+ options.limit = 10;
492
+ }
493
+
494
+ executeTimelineLog(options);
495
+ }
496
+
497
+ /**
498
+ * 快速日志查看
499
+ */
500
+ export async function quickLog(limit: number = 10): Promise<void> {
501
+ const options: LogOptions = { limit };
502
+ executeTimelineLog(options);
503
+ }
@@ -16,11 +16,15 @@ export async function listTags(prefix?: string): Promise<void> {
16
16
 
17
17
  // 2. 获取 tags 列表(按版本号升序排序,最新的在最后)
18
18
  const pattern = prefix ? `${prefix}*` : "";
19
- const tags = execOutput(`git tag -l ${pattern} --sort=v:refname`)
19
+ const allTags = execOutput(`git tag -l ${pattern} --sort=v:refname`)
20
20
  .split("\n")
21
21
  .filter(Boolean);
22
22
 
23
- // 3. 如果没有 tags,提示并返回
23
+ // 3. 过滤无效 tag(如 vnull、vundefined 等误操作产生的 tag)
24
+ // 有效 tag 必须包含数字(版本号)
25
+ const tags = allTags.filter((tag) => /\d/.test(tag));
26
+
27
+ // 4. 如果没有 tags,提示并返回
24
28
  if (tags.length === 0) {
25
29
  console.log(
26
30
  colors.yellow(prefix ? `没有 '${prefix}' 开头的 tag` : "没有 tag")
@@ -39,11 +43,12 @@ export async function listTags(prefix?: string): Promise<void> {
39
43
  return;
40
44
  }
41
45
 
42
- // 5. 按前缀分组(提取 tag 名称中数字前的部分作为前缀)
46
+ // 6. 按前缀分组(提取 tag 名称中数字前的部分作为前缀)
47
+ // 由于已过滤无效 tag,所有 tag 都包含数字
43
48
  const grouped = new Map<string, string[]>();
44
49
  tags.forEach((tag) => {
45
- // 提取前缀:去掉数字及之后的部分,如 "v0.1.0" -> "v"
46
- const prefix = tag.replace(/[0-9].*/, "") || "(无前缀)";
50
+ // 提取数字之前的字母部分作为前缀(如 "v0.1.0" -> "v"
51
+ const prefix = tag.replace(/\d.*/, "") || "(无前缀)";
47
52
  if (!grouped.has(prefix)) {
48
53
  grouped.set(prefix, []);
49
54
  }
@@ -131,11 +136,11 @@ interface TagChoice {
131
136
  value: string;
132
137
  }
133
138
 
134
- // 获取指定前缀的最新 tag(不依赖 shell 管道)
139
+ // 获取指定前缀的最新有效 tag(必须包含数字)
135
140
  function getLatestTag(prefix: string): string {
136
141
  const tags = execOutput(`git tag -l "${prefix}*" --sort=-v:refname`)
137
142
  .split("\n")
138
- .filter(Boolean);
143
+ .filter((tag) => tag && /\d/.test(tag)); // 过滤无效 tag
139
144
  return tags[0] || "";
140
145
  }
141
146
 
@@ -156,7 +161,10 @@ export async function createTag(inputPrefix?: string): Promise<void> {
156
161
  }
157
162
 
158
163
  if (!prefix) {
159
- const allTags = execOutput("git tag -l").split("\n").filter(Boolean);
164
+ // 过滤无效 tag(如 vnull、vundefined 等误操作产生的 tag
165
+ const allTags = execOutput("git tag -l")
166
+ .split("\n")
167
+ .filter((tag) => tag && /\d/.test(tag));
160
168
 
161
169
  // 仓库没有任何 tag 的情况
162
170
  if (allTags.length === 0) {
@@ -209,9 +217,9 @@ export async function createTag(inputPrefix?: string): Promise<void> {
209
217
  return;
210
218
  }
211
219
 
212
- // 从现有 tag 中提取前缀
220
+ // 从现有 tag 中提取前缀(数字之前的字母部分)
213
221
  const prefixes = [
214
- ...new Set(allTags.map((t) => t.replace(/[0-9].*/, "")).filter(Boolean)),
222
+ ...new Set(allTags.map((t) => t.replace(/\d.*/, "")).filter(Boolean)),
215
223
  ];
216
224
 
217
225
  if (prefixes.length === 0) {
package/src/config.ts CHANGED
@@ -46,6 +46,7 @@ export interface GwConfig {
46
46
  language?: "zh-CN" | "en-US"; // 生成语言,默认 zh-CN
47
47
  maxTokens?: number; // 最大 token 数,默认 200
48
48
  detailedDescription?: boolean; // 是否生成详细的修改点描述,默认 false
49
+ useEmoji?: boolean; // AI生成的commit message是否包含emoji,默认继承全局useEmoji设置
49
50
  };
50
51
  }
51
52
 
package/src/index.ts CHANGED
@@ -20,9 +20,9 @@ import { release } from "./commands/release.js";
20
20
  import { init } from "./commands/init.js";
21
21
  import { stash } from "./commands/stash.js";
22
22
  import { commit } from "./commands/commit.js";
23
- import { showHelp } from "./commands/help.js";
24
23
  import { checkForUpdates } from "./update-notifier.js";
25
24
  import { update } from "./commands/update.js";
25
+ import { log, quickLog } from "./commands/log.js";
26
26
 
27
27
  // ========== 全局错误处理 ==========
28
28
 
@@ -151,7 +151,11 @@ async function mainMenu(): Promise<void> {
151
151
  value: "stash",
152
152
  },
153
153
  {
154
- name: `[b] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
154
+ name: `[b] 📊 查看日志 ${colors.dim("gw log")}`,
155
+ value: "log",
156
+ },
157
+ {
158
+ name: `[c] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
155
159
  value: "init",
156
160
  },
157
161
  { name: "[0] ❓ 帮助", value: "help" },
@@ -201,11 +205,16 @@ async function mainMenu(): Promise<void> {
201
205
  checkGitRepo();
202
206
  await stash();
203
207
  break;
208
+ case "log":
209
+ checkGitRepo();
210
+ await log();
211
+ break;
204
212
  case "init":
205
213
  await init();
206
214
  break;
207
215
  case "help":
208
- console.log(showHelp());
216
+ // 使用 cac 自动生成的帮助信息
217
+ cli.outputHelp();
209
218
  break;
210
219
  case "exit":
211
220
  break;
@@ -339,6 +348,22 @@ cli
339
348
  return update(version);
340
349
  });
341
350
 
351
+ cli
352
+ .command("log", "交互式Git日志查看 (分页模式)")
353
+ .alias("ls")
354
+ .alias("l")
355
+ .option("--limit <number>", "限制显示数量")
356
+ .action(async (options: any) => {
357
+ await checkForUpdates(version, "@zjex/git-workflow");
358
+ checkGitRepo();
359
+
360
+ // 构建选项对象 - 默认交互式模式
361
+ const logOptions: any = { interactive: true };
362
+ if (options.limit) logOptions.limit = parseInt(options.limit);
363
+
364
+ return log(logOptions);
365
+ });
366
+
342
367
  cli.command("clean", "清理缓存文件").action(async () => {
343
368
  const { clearUpdateCache } = await import("./update-notifier.js");
344
369
  clearUpdateCache();
@@ -347,20 +372,19 @@ cli.command("clean", "清理缓存文件").action(async () => {
347
372
  console.log("");
348
373
  });
349
374
 
350
- cli.help((sections) => {
351
- sections.push({
352
- body: showHelp(),
353
- });
354
- });
355
-
356
- // 不使用 cac 的 version,手动处理 --version
375
+ // 不使用 cac 的 version,手动处理 --version 和 --help
357
376
  cli.option("-v, --version", "显示版本号");
377
+ cli.option("-h, --help", "显示帮助信息");
358
378
 
359
- // 在 parse 之前检查 --version
360
- const args = process.argv.slice(2);
361
- if (args.includes("-v") || args.includes("--version")) {
379
+ // 在 parse 之前检查 --version 和 --help
380
+ const processArgs = process.argv.slice(2);
381
+ if (processArgs.includes("-v") || processArgs.includes("--version")) {
362
382
  console.log(colors.yellow(`v${version}`));
363
383
  process.exit(0);
364
384
  }
385
+ if (processArgs.includes("-h") || processArgs.includes("--help")) {
386
+ cli.outputHelp();
387
+ process.exit(0);
388
+ }
365
389
 
366
390
  cli.parse();
package/src/utils.ts CHANGED
@@ -4,9 +4,14 @@ export interface Colors {
4
4
  red: (s: string) => string;
5
5
  green: (s: string) => string;
6
6
  yellow: (s: string) => string;
7
+ blue: (s: string) => string;
7
8
  cyan: (s: string) => string;
8
9
  dim: (s: string) => string;
9
10
  bold: (s: string) => string;
11
+ purple: (s: string) => string;
12
+ orange: (s: string) => string;
13
+ lightPurple: (s: string) => string;
14
+ white: (s: string) => string;
10
15
  reset: string;
11
16
  }
12
17
 
@@ -14,9 +19,14 @@ export const colors: Colors = {
14
19
  red: (s) => `\x1b[31m${s}\x1b[0m`,
15
20
  green: (s) => `\x1b[32m${s}\x1b[0m`,
16
21
  yellow: (s) => `\x1b[33m${s}\x1b[0m`,
22
+ blue: (s) => `\x1b[34m${s}\x1b[0m`,
17
23
  cyan: (s) => `\x1b[36m${s}\x1b[0m`,
18
24
  dim: (s) => `\x1b[2m${s}\x1b[0m`,
19
25
  bold: (s) => `\x1b[1m${s}\x1b[0m`,
26
+ purple: (s) => `\x1b[35m${s}\x1b[0m`,
27
+ orange: (s) => `\x1b[38;5;208m${s}\x1b[0m`,
28
+ lightPurple: (s) => `\x1b[38;5;141m${s}\x1b[0m`,
29
+ white: (s) => `\x1b[37m${s}\x1b[0m`,
20
30
  reset: "\x1b[0m",
21
31
  };
22
32