@zjex/git-workflow 0.4.6 → 0.5.0

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 (36) hide show
  1. package/CHANGELOG.md +136 -377
  2. package/README.md +44 -6
  3. package/dist/index.js +675 -31
  4. package/docs/.vitepress/cache/deps/_metadata.json +10 -10
  5. package/docs/.vitepress/config.ts +4 -0
  6. package/docs/commands/index.md +4 -0
  7. package/docs/commands/review.md +142 -0
  8. package/docs/guide/ai-review.md +159 -0
  9. package/docs/guide/index.md +2 -0
  10. package/docs/index.md +26 -3
  11. package/package.json +1 -1
  12. package/scripts/generate-changelog-manual.js +15 -64
  13. package/src/commands/review.ts +759 -0
  14. package/src/commands/tag.ts +42 -10
  15. package/src/commands/update.ts +25 -9
  16. package/src/index.ts +29 -1
  17. package/src/utils.ts +3 -1
  18. package/tests/review.test.ts +1058 -0
  19. package/tests/update.test.ts +85 -69
  20. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-2CLQ7TTZ.js +0 -9719
  21. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-2CLQ7TTZ.js.map +0 -7
  22. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-LE5NDSFD.js +0 -12824
  23. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-LE5NDSFD.js.map +0 -7
  24. package/docs/.vitepress/cache/deps_temp_44e2fb0f/package.json +0 -3
  25. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vue_devtools-api.js +0 -4505
  26. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vue_devtools-api.js.map +0 -7
  27. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_core.js +0 -583
  28. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_core.js.map +0 -7
  29. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_integrations_useFocusTrap.js +0 -1352
  30. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_integrations_useFocusTrap.js.map +0 -7
  31. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___mark__js_src_vanilla__js.js +0 -1665
  32. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___mark__js_src_vanilla__js.js.map +0 -7
  33. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___minisearch.js +0 -1813
  34. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___minisearch.js.map +0 -7
  35. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vue.js +0 -347
  36. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vue.js.map +0 -7
@@ -418,6 +418,29 @@ export async function createTag(inputPrefix?: string): Promise<void> {
418
418
  async function doCreateTag(tagName: string): Promise<void> {
419
419
  divider();
420
420
 
421
+ // 检查是否有提交
422
+ const hasCommits = execOutput("git rev-parse HEAD 2>/dev/null");
423
+ if (!hasCommits) {
424
+ console.log(colors.red("当前仓库没有任何提交"));
425
+ console.log("");
426
+ console.log(colors.dim(" 提示: 需要先创建至少一个提交才能打 tag:"));
427
+ console.log(colors.cyan(" git add ."));
428
+ console.log(colors.cyan(' git commit -m "Initial commit"'));
429
+ console.log(colors.cyan(" gw tag"));
430
+ return;
431
+ }
432
+
433
+ // 检查 tag 是否已存在
434
+ const existingTags = execOutput("git tag -l").split("\n").filter(Boolean);
435
+ if (existingTags.includes(tagName)) {
436
+ console.log(colors.red(`Tag ${tagName} 已存在`));
437
+ console.log("");
438
+ console.log(colors.dim(" 提示: 如需重新创建,请先删除旧 tag:"));
439
+ console.log(colors.cyan(` git tag -d ${tagName}`));
440
+ console.log(colors.cyan(` git push origin --delete ${tagName}`));
441
+ return;
442
+ }
443
+
421
444
  const spinner = ora(`正在创建 tag: ${tagName}`).start();
422
445
  const success = await execWithSpinner(
423
446
  `git tag -a "${tagName}" -m "Release ${tagName}"`,
@@ -614,9 +637,12 @@ export async function updateTag(): Promise<void> {
614
637
  }
615
638
 
616
639
  // 删除旧 tag
617
- const deleteSuccess = await execAsync(`git tag -d "${oldTag}"`, spinner);
618
- if (!deleteSuccess) {
640
+ const deleteResult = await execAsync(`git tag -d "${oldTag}"`, spinner);
641
+ if (!deleteResult.success) {
619
642
  spinner.fail("删除旧 tag 失败");
643
+ if (deleteResult.error) {
644
+ console.log(colors.dim(` ${deleteResult.error}`));
645
+ }
620
646
  return;
621
647
  }
622
648
 
@@ -635,26 +661,32 @@ export async function updateTag(): Promise<void> {
635
661
  const pushSpinner = ora("正在同步到远程...").start();
636
662
 
637
663
  // 推送新 tag
638
- const pushNewSuccess = await execAsync(
664
+ const pushNewResult = await execAsync(
639
665
  `git push origin "${newTag}"`,
640
666
  pushSpinner,
641
667
  );
642
- if (!pushNewSuccess) {
668
+ if (!pushNewResult.success) {
643
669
  pushSpinner.warn(
644
670
  `远程同步失败,可稍后手动执行:\n git push origin ${newTag}\n git push origin --delete ${oldTag}`,
645
671
  );
672
+ if (pushNewResult.error) {
673
+ console.log(colors.dim(` ${pushNewResult.error}`));
674
+ }
646
675
  return;
647
676
  }
648
677
 
649
678
  // 删除远程旧 tag
650
- const deleteOldSuccess = await execAsync(
679
+ const deleteOldResult = await execAsync(
651
680
  `git push origin --delete "${oldTag}"`,
652
681
  pushSpinner,
653
682
  );
654
- if (!deleteOldSuccess) {
683
+ if (!deleteOldResult.success) {
655
684
  pushSpinner.warn(
656
685
  `远程旧 tag 删除失败,可稍后手动执行: git push origin --delete ${oldTag}`,
657
686
  );
687
+ if (deleteOldResult.error) {
688
+ console.log(colors.dim(` ${deleteOldResult.error}`));
689
+ }
658
690
  return;
659
691
  }
660
692
 
@@ -729,8 +761,8 @@ export async function cleanInvalidTags(): Promise<void> {
729
761
  let localFailed = 0;
730
762
 
731
763
  for (const tag of invalidTags) {
732
- const success = await execAsync(`git tag -d "${tag}"`, localSpinner);
733
- if (success) {
764
+ const result = await execAsync(`git tag -d "${tag}"`, localSpinner);
765
+ if (result.success) {
734
766
  localSuccess++;
735
767
  } else {
736
768
  localFailed++;
@@ -761,11 +793,11 @@ export async function cleanInvalidTags(): Promise<void> {
761
793
  let remoteFailed = 0;
762
794
 
763
795
  for (const tag of invalidTags) {
764
- const success = await execAsync(
796
+ const result = await execAsync(
765
797
  `git push origin --delete "${tag}"`,
766
798
  remoteSpinner,
767
799
  );
768
- if (success) {
800
+ if (result.success) {
769
801
  remoteSuccess++;
770
802
  } else {
771
803
  remoteFailed++;
@@ -27,16 +27,32 @@ function clearUpdateCache(): void {
27
27
  * 获取 npm 上的最新版本
28
28
  */
29
29
  async function getLatestVersion(packageName: string): Promise<string | null> {
30
- try {
31
- const result = execSync(`npm view ${packageName} version`, {
32
- encoding: "utf-8",
33
- timeout: 3000,
34
- stdio: ["pipe", "pipe", "ignore"],
30
+ return new Promise((resolve) => {
31
+ const npmView = spawn("npm", ["view", packageName, "version"], {
32
+ stdio: ["ignore", "pipe", "ignore"],
33
+ timeout: 5000,
35
34
  });
36
- return result.trim();
37
- } catch {
38
- return null;
39
- }
35
+
36
+ let output = "";
37
+
38
+ if (npmView.stdout) {
39
+ npmView.stdout.on("data", (data) => {
40
+ output += data.toString();
41
+ });
42
+ }
43
+
44
+ npmView.on("close", (code) => {
45
+ if (code === 0 && output.trim()) {
46
+ resolve(output.trim());
47
+ } else {
48
+ resolve(null);
49
+ }
50
+ });
51
+
52
+ npmView.on("error", () => {
53
+ resolve(null);
54
+ });
55
+ });
40
56
  }
41
57
 
42
58
  /**
package/src/index.ts CHANGED
@@ -31,6 +31,7 @@ import { update } from "./commands/update.js";
31
31
  import { log, quickLog } from "./commands/log.js";
32
32
  import { amendDate } from "./commands/amend-date.js";
33
33
  import { amend } from "./commands/amend.js";
34
+ import { review } from "./commands/review.js";
34
35
 
35
36
  // ========== 全局错误处理 ==========
36
37
 
@@ -171,7 +172,11 @@ async function mainMenu(): Promise<void> {
171
172
  value: "amend",
172
173
  },
173
174
  {
174
- name: `[e] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
175
+ name: `[e] 🔍 AI 代码审查 ${colors.dim("gw review")}`,
176
+ value: "review",
177
+ },
178
+ {
179
+ name: `[f] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
175
180
  value: "init",
176
181
  },
177
182
  { name: "[0] ❓ 帮助", value: "help" },
@@ -233,6 +238,10 @@ async function mainMenu(): Promise<void> {
233
238
  checkGitRepo();
234
239
  await amend();
235
240
  break;
241
+ case "review":
242
+ checkGitRepo();
243
+ await review();
244
+ break;
236
245
  case "init":
237
246
  await init();
238
247
  break;
@@ -413,6 +422,25 @@ cli
413
422
  return amend(hash);
414
423
  });
415
424
 
425
+ cli
426
+ .command("review [...hashes]", "AI 代码审查")
427
+ .alias("rw")
428
+ .option("-n, --last <number>", "审查最近 N 个 commits")
429
+ .option("-s, --staged", "审查暂存区的更改")
430
+ .option("-o, --output <path>", "指定输出文件路径")
431
+ .action(async (hashes: string[], options: any) => {
432
+ await checkForUpdates(version, "@zjex/git-workflow");
433
+ checkGitRepo();
434
+ return review(
435
+ hashes.length > 0 ? hashes : undefined,
436
+ {
437
+ last: options.last ? parseInt(options.last) : undefined,
438
+ staged: options.staged,
439
+ output: options.output,
440
+ }
441
+ );
442
+ });
443
+
416
444
  cli
417
445
  .command("clean", "清理缓存和临时文件")
418
446
  .alias("cc")
package/src/utils.ts CHANGED
@@ -166,7 +166,9 @@ export function execAsync(
166
166
  console.log(colors.dim(`[DEBUG] 标准输出:\n${stdoutOutput}`));
167
167
  }
168
168
  if (errorOutput) {
169
- console.log(colors.dim(`[DEBUG] 错误输出:\n${errorOutput}`));
169
+ // 根据退出码决定标签:成功时显示"输出信息",失败时显示"错误输出"
170
+ const label = code === 0 ? "输出信息" : "错误输出";
171
+ console.log(colors.dim(`[DEBUG] ${label}:\n${errorOutput}`));
170
172
  }
171
173
  }
172
174