@zjex/git-workflow 0.2.24 → 0.3.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.
Files changed (99) hide show
  1. package/.github/workflows/deploy-docs.yml +68 -0
  2. package/.github/workflows/test.yml +24 -4
  3. package/.husky/pre-commit +17 -0
  4. package/README.md +72 -1066
  5. package/ROADMAP.md +275 -0
  6. package/dist/index.js +450 -99
  7. package/docs/.vitepress/cache/deps/_metadata.json +52 -0
  8. package/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js +9719 -0
  9. package/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js.map +7 -0
  10. package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js +12824 -0
  11. package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js.map +7 -0
  12. package/docs/.vitepress/cache/deps/package.json +3 -0
  13. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4505 -0
  14. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  15. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +583 -0
  16. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  17. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1352 -0
  18. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  19. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1665 -0
  20. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  21. package/docs/.vitepress/cache/deps/vitepress___minisearch.js +1813 -0
  22. package/docs/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
  23. package/docs/.vitepress/cache/deps/vue.js +347 -0
  24. package/docs/.vitepress/cache/deps/vue.js.map +7 -0
  25. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-2CLQ7TTZ.js +9719 -0
  26. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-2CLQ7TTZ.js.map +7 -0
  27. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-LE5NDSFD.js +12824 -0
  28. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-LE5NDSFD.js.map +7 -0
  29. package/docs/.vitepress/cache/deps_temp_44e2fb0f/package.json +3 -0
  30. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vue_devtools-api.js +4505 -0
  31. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vue_devtools-api.js.map +7 -0
  32. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_core.js +583 -0
  33. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_core.js.map +7 -0
  34. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_integrations_useFocusTrap.js +1352 -0
  35. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  36. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___mark__js_src_vanilla__js.js +1665 -0
  37. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  38. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___minisearch.js +1813 -0
  39. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___minisearch.js.map +7 -0
  40. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vue.js +347 -0
  41. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vue.js.map +7 -0
  42. package/docs/.vitepress/config.ts +167 -0
  43. package/docs/.vitepress/theme/custom.css +39 -0
  44. package/docs/.vitepress/theme/index.ts +4 -0
  45. package/docs/README.md +82 -0
  46. package/docs/commands/branch.md +468 -0
  47. package/docs/commands/commit.md +554 -0
  48. package/docs/commands/config.md +346 -0
  49. package/docs/commands/index.md +312 -0
  50. package/docs/commands/interactive.md +384 -0
  51. package/docs/commands/release.md +300 -0
  52. package/docs/commands/stash.md +309 -0
  53. package/docs/commands/tag.md +278 -0
  54. package/docs/commands/update.md +347 -0
  55. package/docs/config/ai-config.md +160 -0
  56. package/docs/config/branch-config.md +133 -0
  57. package/docs/config/commit-config.md +185 -0
  58. package/docs/config/config-file.md +776 -0
  59. package/docs/config/examples.md +279 -0
  60. package/docs/config/index.md +478 -0
  61. package/docs/features/git-wrapped.md +199 -0
  62. package/docs/guide/ai-commit.md +576 -0
  63. package/docs/guide/basic-usage.md +522 -0
  64. package/docs/guide/best-practices.md +426 -0
  65. package/docs/guide/branch-management.md +712 -0
  66. package/docs/guide/getting-started.md +294 -0
  67. package/docs/guide/index.md +168 -0
  68. package/docs/guide/installation.md +449 -0
  69. package/docs/guide/release-management.md +744 -0
  70. package/docs/guide/stash-management.md +608 -0
  71. package/docs/guide/tag-management.md +614 -0
  72. package/docs/index.md +205 -0
  73. package/docs/public/favicon.svg +21 -0
  74. package/docs/public/hero-logo.svg +43 -0
  75. package/docs/public/logo.svg +20 -0
  76. package/package.json +12 -2
  77. package/scripts/publish.js +55 -8
  78. package/scripts/publish.sh +20 -2
  79. package/scripts/release.sh +81 -3
  80. package/scripts/update-test-count.js +55 -0
  81. package/src/ai-service.ts +107 -15
  82. package/src/commands/commit.ts +4 -0
  83. package/src/commands/init.ts +18 -0
  84. package/src/commands/log.ts +503 -0
  85. package/src/config.ts +1 -0
  86. package/src/index.ts +37 -13
  87. package/src/utils.ts +10 -0
  88. package/tests/ai-service.test.ts +237 -2
  89. package/tests/init.test.ts +582 -0
  90. package/tests/log.test.ts +106 -0
  91. package/tests/release.test.ts +333 -0
  92. package/tests/setup.ts +21 -0
  93. package/tests/stash.test.ts +376 -0
  94. package/tests/update.test.ts +402 -0
  95. package/vitest.config.ts +3 -0
  96. package/zjex-logo.svg +22 -0
  97. package/zjex-optimized.svg +34 -0
  98. package/zjex.svg +1 -0
  99. package/src/commands/help.ts +0 -76
@@ -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
+ }
package/src/config.ts CHANGED
@@ -45,6 +45,7 @@ export interface GwConfig {
45
45
  model?: string; // 模型名称
46
46
  language?: "zh-CN" | "en-US"; // 生成语言,默认 zh-CN
47
47
  maxTokens?: number; // 最大 token 数,默认 200
48
+ detailedDescription?: boolean; // 是否生成详细的修改点描述,默认 false
48
49
  };
49
50
  }
50
51
 
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