clawt 2.20.0 → 3.1.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 (77) hide show
  1. package/.claude/agents/docs-sync-updater.md +29 -11
  2. package/README.md +19 -30
  3. package/dist/index.js +1127 -222
  4. package/dist/postinstall.js +73 -8
  5. package/docs/alias.md +108 -0
  6. package/docs/completion.md +55 -0
  7. package/docs/config-file.md +43 -0
  8. package/docs/config.md +91 -0
  9. package/docs/create.md +85 -0
  10. package/docs/init.md +65 -0
  11. package/docs/list.md +67 -0
  12. package/docs/log.md +67 -0
  13. package/docs/merge.md +137 -0
  14. package/docs/notification.md +94 -0
  15. package/docs/projects.md +135 -0
  16. package/docs/remove.md +79 -0
  17. package/docs/reset.md +35 -0
  18. package/docs/resume.md +99 -0
  19. package/docs/run.md +146 -0
  20. package/docs/spec.md +157 -1906
  21. package/docs/status.md +298 -0
  22. package/docs/sync.md +114 -0
  23. package/docs/update-check.md +95 -0
  24. package/docs/validate.md +368 -0
  25. package/package.json +1 -1
  26. package/src/commands/alias.ts +1 -1
  27. package/src/commands/create.ts +10 -5
  28. package/src/commands/init.ts +75 -0
  29. package/src/commands/list.ts +1 -1
  30. package/src/commands/merge.ts +11 -4
  31. package/src/commands/remove.ts +10 -3
  32. package/src/commands/reset.ts +3 -0
  33. package/src/commands/resume.ts +1 -1
  34. package/src/commands/run.ts +9 -3
  35. package/src/commands/status.ts +14 -5
  36. package/src/commands/sync.ts +18 -6
  37. package/src/commands/validate.ts +46 -52
  38. package/src/constants/branch.ts +3 -0
  39. package/src/constants/config.ts +1 -1
  40. package/src/constants/index.ts +14 -2
  41. package/src/constants/interactive-panel.ts +44 -0
  42. package/src/constants/messages/completion.ts +1 -1
  43. package/src/constants/messages/create.ts +3 -0
  44. package/src/constants/messages/index.ts +4 -0
  45. package/src/constants/messages/init.ts +18 -0
  46. package/src/constants/messages/interactive-panel.ts +61 -0
  47. package/src/constants/messages/remove.ts +2 -0
  48. package/src/constants/messages/sync.ts +3 -0
  49. package/src/constants/messages/validate.ts +6 -0
  50. package/src/constants/paths.ts +3 -0
  51. package/src/index.ts +2 -0
  52. package/src/types/command.ts +9 -1
  53. package/src/types/index.ts +2 -1
  54. package/src/types/projectConfig.ts +5 -0
  55. package/src/utils/config.ts +2 -1
  56. package/src/utils/git.ts +18 -0
  57. package/src/utils/index.ts +9 -1
  58. package/src/utils/interactive-panel-render.ts +315 -0
  59. package/src/utils/interactive-panel.ts +590 -0
  60. package/src/utils/json.ts +67 -0
  61. package/src/utils/project-config.ts +77 -0
  62. package/src/utils/validate-branch.ts +166 -0
  63. package/src/utils/worktree-matcher.ts +2 -2
  64. package/src/utils/worktree.ts +6 -2
  65. package/tests/unit/commands/create.test.ts +20 -16
  66. package/tests/unit/commands/init.test.ts +146 -0
  67. package/tests/unit/commands/merge.test.ts +7 -1
  68. package/tests/unit/commands/remove.test.ts +4 -0
  69. package/tests/unit/commands/reset.test.ts +2 -0
  70. package/tests/unit/commands/run.test.ts +2 -0
  71. package/tests/unit/commands/sync.test.ts +6 -0
  72. package/tests/unit/commands/validate.test.ts +13 -0
  73. package/tests/unit/utils/config.test.ts +2 -2
  74. package/tests/unit/utils/project-config.test.ts +136 -0
  75. package/tests/unit/utils/update-checker.test.ts +28 -7
  76. package/tests/unit/utils/validate-branch.test.ts +272 -0
  77. package/tests/unit/utils/worktree.test.ts +6 -0
package/dist/index.js CHANGED
@@ -16,9 +16,11 @@ var WORKTREES_DIR = join(CLAWT_HOME, "worktrees");
16
16
  var VALIDATE_SNAPSHOTS_DIR = join(CLAWT_HOME, "validate-snapshots");
17
17
  var CLAUDE_PROJECTS_DIR = join(homedir(), ".claude", "projects");
18
18
  var UPDATE_CHECK_PATH = join(CLAWT_HOME, "update-check.json");
19
+ var PROJECTS_CONFIG_DIR = join(CLAWT_HOME, "projects");
19
20
 
20
21
  // src/constants/branch.ts
21
22
  var INVALID_BRANCH_CHARS = /[\/\\.\s~:*?[\]^]+/g;
23
+ var VALIDATE_BRANCH_PREFIX = "clawt-validate-";
22
24
 
23
25
  // src/constants/messages/common.ts
24
26
  var COMMON_MESSAGES = {
@@ -121,7 +123,9 @@ var RUN_MESSAGES = {
121
123
  // src/constants/messages/create.ts
122
124
  var CREATE_MESSAGES = {
123
125
  /** 创建数量参数无效 */
124
- INVALID_COUNT: (value) => `\u65E0\u6548\u7684\u521B\u5EFA\u6570\u91CF: "${value}"\uFF0C\u8BF7\u8F93\u5165\u6B63\u6574\u6570`
126
+ INVALID_COUNT: (value) => `\u65E0\u6548\u7684\u521B\u5EFA\u6570\u91CF: "${value}"\uFF0C\u8BF7\u8F93\u5165\u6B63\u6574\u6570`,
127
+ /** 当前不在主工作分支上的警告 */
128
+ CREATE_WARN_NOT_ON_MAIN_BRANCH: (mainBranch, currentBranch) => `\u5F53\u524D\u4E0D\u5728\u4E3B\u5DE5\u4F5C\u5206\u652F ${mainBranch} \u4E0A\uFF08\u5F53\u524D: ${currentBranch}\uFF09`
125
129
  };
126
130
 
127
131
  // src/constants/messages/merge.ts
@@ -218,7 +222,12 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
218
222
  /** 自动 sync 开始提示 */
219
223
  VALIDATE_AUTO_SYNC_START: (branch) => `\u6B63\u5728\u81EA\u52A8\u540C\u6B65\u4E3B\u5206\u652F\u5230 ${branch} ...`,
220
224
  /** 用户拒绝自动 sync */
221
- VALIDATE_AUTO_SYNC_DECLINED: (branch) => `\u8BF7\u624B\u52A8\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`
225
+ VALIDATE_AUTO_SYNC_DECLINED: (branch) => `\u8BF7\u624B\u52A8\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`,
226
+ /** 验证分支不存在 */
227
+ VALIDATE_BRANCH_NOT_FOUND: (validateBranch, branch) => `\u9A8C\u8BC1\u5206\u652F ${validateBranch} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C clawt create \u6216 clawt run \u521B\u5EFA\u5206\u652F ${branch}`,
228
+ /** validate 成功(含验证分支信息) */
229
+ VALIDATE_SUCCESS_WITH_BRANCH: (branch, validateBranch) => `\u2713 \u5DF2\u5207\u6362\u5230\u9A8C\u8BC1\u5206\u652F ${validateBranch} \u5E76\u5E94\u7528\u5206\u652F ${branch} \u7684\u53D8\u66F4
230
+ \u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86`
222
231
  };
223
232
 
224
233
  // src/constants/messages/sync.ts
@@ -243,7 +252,9 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
243
252
  /** sync 交互选择提示 */
244
253
  SYNC_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u540C\u6B65\u7684\u5206\u652F",
245
254
  /** sync 模糊匹配到多个结果提示 */
246
- SYNC_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
255
+ SYNC_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`,
256
+ /** sync 后验证分支已重建提示 */
257
+ SYNC_VALIDATE_BRANCH_REBUILT: (validateBranch) => `\u9A8C\u8BC1\u5206\u652F ${validateBranch} \u5DF2\u91CD\u5EFA`
247
258
  };
248
259
 
249
260
  // src/constants/messages/resume.ts
@@ -284,7 +295,9 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
284
295
  REMOVE_PARTIAL_FAILURE: (failures) => `\u4EE5\u4E0B worktree \u79FB\u9664\u5931\u8D25\uFF1A
285
296
  ${failures.map((f) => ` \u2717 ${f.path}: ${f.error}`).join("\n")}`,
286
297
  /** 用户选择保留本地分支 */
287
- REMOVE_BRANCHES_KEPT: "\u5DF2\u4FDD\u7559\u672C\u5730\u5206\u652F\uFF0C\u53EF\u7A0D\u540E\u4F7F\u7528 git branch -D <\u5206\u652F\u540D> \u624B\u52A8\u5220\u9664"
298
+ REMOVE_BRANCHES_KEPT: "\u5DF2\u4FDD\u7559\u672C\u5730\u5206\u652F\uFF0C\u53EF\u7A0D\u540E\u4F7F\u7528 git branch -D <\u5206\u652F\u540D> \u624B\u52A8\u5220\u9664",
299
+ /** 确认删除本地分支和验证分支 */
300
+ REMOVE_CONFIRM_DELETE_BRANCHES: "\u662F\u5426\u540C\u65F6\u5220\u9664\u5BF9\u5E94\u7684\u672C\u5730\u5206\u652F\u548C\u9A8C\u8BC1\u5206\u652F\uFF1F"
288
301
  };
289
302
 
290
303
  // src/constants/messages/reset.ts
@@ -402,7 +415,7 @@ var PROJECTS_MESSAGES = {
402
415
  // src/constants/messages/completion.ts
403
416
  var COMPLETION_MESSAGES = {
404
417
  /** completion 命令的主描述 */
405
- COMPLETION_COMMAND_DESC: "\u751F\u6210\u548C\u5B89\u88C5 shell \u81EA\u52A8\u8865\u5168\u811A\u672C",
418
+ COMPLETION_COMMAND_DESC: "\u4E3A\u7EC8\u7AEF\u63D0\u4F9B shell \u81EA\u52A8\u8865\u5168\u529F\u80FD\uFF08bash/zsh\uFF09",
406
419
  /** bash 子命令描述 */
407
420
  COMPLETION_BASH_DESC: "\u8F93\u51FA bash \u81EA\u52A8\u8865\u5168\u811A\u672C",
408
421
  /** zsh 子命令描述 */
@@ -434,6 +447,78 @@ var UPDATE_MESSAGES = {
434
447
  UPDATE_HINT: (command) => `\u6267\u884C ${command} \u8FDB\u884C\u66F4\u65B0`
435
448
  };
436
449
 
450
+ // src/constants/messages/init.ts
451
+ var INIT_MESSAGES = {
452
+ /** init 成功 */
453
+ INIT_SUCCESS: (branch) => `\u2713 \u9879\u76EE\u521D\u59CB\u5316\u6210\u529F\uFF0C\u4E3B\u5DE5\u4F5C\u5206\u652F\u8BBE\u7F6E\u4E3A: ${branch}`,
454
+ /** init 更新 */
455
+ INIT_UPDATED: (oldBranch, newBranch) => `\u2713 \u5DF2\u5C06\u4E3B\u5DE5\u4F5C\u5206\u652F\u4ECE ${oldBranch} \u66F4\u65B0\u4E3A ${newBranch}`,
456
+ /** init show 查看当前项目完整配置(JSON 格式) */
457
+ INIT_SHOW: (configJson) => `${configJson}`,
458
+ /** init 未初始化 */
459
+ INIT_NOT_INITIALIZED: "\u9879\u76EE\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
460
+ /** 项目未初始化(requireProjectConfig 使用) */
461
+ PROJECT_NOT_INITIALIZED: "\u9879\u76EE\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
462
+ /** 项目配置缺少 clawtMainWorkBranch 字段 */
463
+ PROJECT_CONFIG_MISSING_BRANCH: "\u9879\u76EE\u914D\u7F6E\u7F3A\u5C11\u4E3B\u5DE5\u4F5C\u5206\u652F\u4FE1\u606F\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F"
464
+ };
465
+
466
+ // src/constants/messages/interactive-panel.ts
467
+ import chalk from "chalk";
468
+
469
+ // src/constants/interactive-panel.ts
470
+ var PANEL_REFRESH_INTERVAL_MS = 5e3;
471
+ var PANEL_COUNTDOWN_INTERVAL_MS = 1e3;
472
+ var SELECTED_INDICATOR = "\u25B6";
473
+ var UNSELECTED_INDICATOR = " ";
474
+ var KEY_ARROW_UP = "\x1B[A";
475
+ var KEY_ARROW_DOWN = "\x1B[B";
476
+ var KEY_CTRL_C = 3;
477
+ var PANEL_SHORTCUT_KEYS = {
478
+ /** 验证 */
479
+ VALIDATE: "v",
480
+ /** 合并 */
481
+ MERGE: "m",
482
+ /** 删除 */
483
+ DELETE: "d",
484
+ /** 恢复 */
485
+ RESUME: "r",
486
+ /** 同步 */
487
+ SYNC: "s",
488
+ /** 手动刷新 */
489
+ REFRESH: "f",
490
+ /** 退出 */
491
+ QUIT: "q"
492
+ };
493
+ var PANEL_DATE_SEPARATOR_PREFIX = "\u2550\u2550\u2550\u2550";
494
+ var PANEL_FIXED_ROWS = 4;
495
+
496
+ // src/constants/messages/interactive-panel.ts
497
+ var SHORTCUT_LABELS = {
498
+ VALIDATE: "\u9A8C\u8BC1",
499
+ MERGE: "\u5408\u5E76",
500
+ DELETE: "\u5220\u9664",
501
+ RESUME: "\u6062\u590D",
502
+ SYNC: "\u540C\u6B65",
503
+ REFRESH: "\u5237\u65B0",
504
+ QUIT: "\u9000\u51FA"
505
+ };
506
+ var PANEL_FOOTER_SHORTCUTS = Object.entries(SHORTCUT_LABELS).map(([key, label]) => `[${chalk.cyan(PANEL_SHORTCUT_KEYS[key])}]${label}`).join(" ");
507
+ var PANEL_FOOTER_COUNTDOWN = (seconds) => chalk.gray(`(${seconds}s \u540E\u5237\u65B0)`);
508
+ var PANEL_OVERFLOW_DOWN_HINT = chalk.gray("\u2193 \u66F4\u591A worktree...");
509
+ var PANEL_OVERFLOW_UP_HINT = chalk.gray("\u2191 \u66F4\u591A worktree...");
510
+ var PANEL_SNAPSHOT_SUMMARY = (total, orphaned) => {
511
+ const base = `\u5FEB\u7167: ${total} \u4E2A`;
512
+ if (orphaned > 0) {
513
+ return `${base}\uFF08${chalk.yellow(`${orphaned} \u4E2A\u5B64\u7ACB`)}\uFF09`;
514
+ }
515
+ return base;
516
+ };
517
+ var PANEL_NO_WORKTREES = "(\u65E0\u6D3B\u8DC3 worktree)";
518
+ var PANEL_PRESS_ENTER_TO_RETURN = chalk.gray("\n\u6309 Enter \u8FD4\u56DE\u9762\u677F...");
519
+ var PANEL_NOT_TTY = "\u4EA4\u4E92\u5F0F\u9762\u677F\u9700\u8981 TTY \u7EC8\u7AEF\u73AF\u5883\uFF0C\u8BF7\u76F4\u63A5\u5728\u7EC8\u7AEF\u4E2D\u8FD0\u884C clawt status -i";
520
+ var PANEL_TITLE = (projectName) => chalk.bold.cyan(`\u9879\u76EE\u72B6\u6001\u603B\u89C8: ${projectName}`);
521
+
437
522
  // src/constants/messages/index.ts
438
523
  var MESSAGES = {
439
524
  ...COMMON_MESSAGES,
@@ -449,7 +534,8 @@ var MESSAGES = {
449
534
  ...STATUS_MESSAGES,
450
535
  ...ALIAS_MESSAGES,
451
536
  ...PROJECTS_MESSAGES,
452
- ...COMPLETION_MESSAGES
537
+ ...COMPLETION_MESSAGES,
538
+ ...INIT_MESSAGES
453
539
  };
454
540
 
455
541
  // src/constants/exitCodes.ts
@@ -567,14 +653,14 @@ var CLEAR_SCREEN = "\x1B[2J";
567
653
  var CURSOR_HOME = "\x1B[H";
568
654
 
569
655
  // src/constants/prompt.ts
570
- import chalk from "chalk";
656
+ import chalk2 from "chalk";
571
657
  var SELECT_ALL_NAME = "__select_all__";
572
658
  var SELECT_ALL_LABEL = "[select-all]";
573
659
  var GROUP_SELECT_ALL_PREFIX = "__group_select_all_";
574
660
  var GROUP_SELECT_ALL_LABEL = (dateLabel) => `[select-all: ${dateLabel}]`;
575
- var GROUP_SEPARATOR_LABEL = (dateLabel, relativeTime) => `\u2550\u2550\u2550\u2550 ${chalk.bold.hex("#FF8C00")(dateLabel)}\uFF08${chalk.hex("#FF8C00")(relativeTime)}\uFF09 \u2550\u2550\u2550\u2550`;
661
+ var GROUP_SEPARATOR_LABEL = (dateLabel, relativeTime) => `\u2550\u2550\u2550\u2550 ${chalk2.bold.hex("#FF8C00")(dateLabel)}\uFF08${chalk2.hex("#FF8C00")(relativeTime)}\uFF09 \u2550\u2550\u2550\u2550`;
576
662
  var UNKNOWN_DATE_GROUP = "\u672A\u77E5\u65E5\u671F";
577
- var UNKNOWN_DATE_SEPARATOR_LABEL = `\u2550\u2550\u2550\u2550 ${chalk.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} \u2550\u2550\u2550\u2550`;
663
+ var UNKNOWN_DATE_SEPARATOR_LABEL = `\u2550\u2550\u2550\u2550 ${chalk2.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} \u2550\u2550\u2550\u2550`;
578
664
 
579
665
  // src/errors/index.ts
580
666
  var ClawtError = class extends Error {
@@ -594,7 +680,7 @@ var ClawtError = class extends Error {
594
680
  // src/logger/index.ts
595
681
  import winston from "winston";
596
682
  import DailyRotateFile from "winston-daily-rotate-file";
597
- import chalk2 from "chalk";
683
+ import chalk3 from "chalk";
598
684
  import { existsSync, mkdirSync } from "fs";
599
685
  if (!existsSync(LOGS_DIR)) {
600
686
  mkdirSync(LOGS_DIR, { recursive: true });
@@ -619,13 +705,13 @@ var logger = winston.createLogger({
619
705
  transports: [dailyRotateTransport]
620
706
  });
621
707
  var LEVEL_COLORS = {
622
- error: chalk2.red,
623
- warn: chalk2.yellow,
624
- info: chalk2.cyan,
625
- debug: chalk2.gray
708
+ error: chalk3.red,
709
+ warn: chalk3.yellow,
710
+ info: chalk3.cyan,
711
+ debug: chalk3.gray
626
712
  };
627
713
  function colorizeLevel(level) {
628
- const colorFn = LEVEL_COLORS[level] || chalk2.white;
714
+ const colorFn = LEVEL_COLORS[level] || chalk3.white;
629
715
  return colorFn(level.toUpperCase().padEnd(5));
630
716
  }
631
717
  function enableConsoleTransport() {
@@ -636,7 +722,7 @@ function enableConsoleTransport() {
636
722
  return;
637
723
  }
638
724
  const consoleFormat = winston.format.printf(({ level, message, timestamp }) => {
639
- return `${chalk2.gray(timestamp)} ${colorizeLevel(level)} ${message}`;
725
+ return `${chalk3.gray(timestamp)} ${colorizeLevel(level)} ${message}`;
640
726
  });
641
727
  const consoleTransport = new winston.transports.Console({
642
728
  level: "debug",
@@ -902,24 +988,30 @@ function getBranchCreatedAt(branchName, cwd) {
902
988
  return null;
903
989
  }
904
990
  }
991
+ function gitCheckout(branchName, cwd) {
992
+ execCommand(`git checkout ${branchName}`, { cwd });
993
+ }
994
+ function createBranch(branchName, cwd) {
995
+ execCommand(`git branch ${branchName}`, { cwd });
996
+ }
905
997
 
906
998
  // src/utils/formatter.ts
907
- import chalk3 from "chalk";
999
+ import chalk4 from "chalk";
908
1000
  import { createInterface } from "readline";
909
1001
  function printSuccess(message) {
910
- console.log(chalk3.green(message));
1002
+ console.log(chalk4.green(message));
911
1003
  }
912
1004
  function printError(message) {
913
- console.error(chalk3.red(`\u2717 ${message}`));
1005
+ console.error(chalk4.red(`\u2717 ${message}`));
914
1006
  }
915
1007
  function printWarning(message) {
916
- console.log(chalk3.yellow(`\u26A0 ${message}`));
1008
+ console.log(chalk4.yellow(`\u26A0 ${message}`));
917
1009
  }
918
1010
  function printInfo(message) {
919
1011
  console.log(message);
920
1012
  }
921
1013
  function printHint(message) {
922
- console.log(chalk3.hex("#FF8C00")(message));
1014
+ console.log(chalk4.hex("#FF8C00")(message));
923
1015
  }
924
1016
  function printSeparator() {
925
1017
  console.log(MESSAGES.SEPARATOR);
@@ -940,7 +1032,7 @@ function confirmAction(question) {
940
1032
  });
941
1033
  }
942
1034
  function confirmDestructiveAction(dangerousCommand, description) {
943
- printWarning(`\u5373\u5C06\u6267\u884C ${chalk3.red.bold(dangerousCommand)}\uFF0C${description}`);
1035
+ printWarning(`\u5373\u5C06\u6267\u884C ${chalk4.red.bold(dangerousCommand)}\uFF0C${description}`);
944
1036
  return confirmAction("\u662F\u5426\u7EE7\u7EED\uFF1F");
945
1037
  }
946
1038
  function isWorktreeIdle(status) {
@@ -948,17 +1040,17 @@ function isWorktreeIdle(status) {
948
1040
  }
949
1041
  function formatWorktreeStatus(status) {
950
1042
  const parts = [];
951
- parts.push(chalk3.yellow(`${status.commitCount} \u4E2A\u63D0\u4EA4`));
1043
+ parts.push(chalk4.yellow(`${status.commitCount} \u4E2A\u63D0\u4EA4`));
952
1044
  if (status.insertions === 0 && status.deletions === 0) {
953
1045
  parts.push("\u65E0\u53D8\u66F4");
954
1046
  } else {
955
1047
  const diffParts = [];
956
- diffParts.push(chalk3.green(`+${status.insertions}`));
957
- diffParts.push(chalk3.red(`-${status.deletions}`));
1048
+ diffParts.push(chalk4.green(`+${status.insertions}`));
1049
+ diffParts.push(chalk4.red(`-${status.deletions}`));
958
1050
  parts.push(diffParts.join(" "));
959
1051
  }
960
1052
  if (status.hasDirtyFiles) {
961
- parts.push(chalk3.gray("(\u672A\u63D0\u4EA4\u4FEE\u6539)"));
1053
+ parts.push(chalk4.gray("(\u672A\u63D0\u4EA4\u4FEE\u6539)"));
962
1054
  }
963
1055
  return parts.join(" ");
964
1056
  }
@@ -1076,8 +1168,8 @@ function validateClaudeCodeInstalled() {
1076
1168
  }
1077
1169
 
1078
1170
  // src/utils/worktree.ts
1079
- import { join as join3 } from "path";
1080
- import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
1171
+ import { join as join4 } from "path";
1172
+ import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
1081
1173
 
1082
1174
  // src/utils/fs.ts
1083
1175
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmdirSync, statSync } from "fs";
@@ -1118,10 +1210,152 @@ function calculateDirSize(dirPath) {
1118
1210
  return totalSize;
1119
1211
  }
1120
1212
 
1213
+ // src/utils/validate-branch.ts
1214
+ import Enquirer from "enquirer";
1215
+
1216
+ // src/utils/project-config.ts
1217
+ import { existsSync as existsSync3, readFileSync, writeFileSync } from "fs";
1218
+ import { join as join3 } from "path";
1219
+ function getProjectConfigPath(projectName) {
1220
+ return join3(PROJECTS_CONFIG_DIR, projectName, "config.json");
1221
+ }
1222
+ function loadProjectConfig() {
1223
+ const projectName = getProjectName();
1224
+ const configPath = getProjectConfigPath(projectName);
1225
+ if (!existsSync3(configPath)) {
1226
+ return null;
1227
+ }
1228
+ try {
1229
+ const raw = readFileSync(configPath, "utf-8");
1230
+ return JSON.parse(raw);
1231
+ } catch (error) {
1232
+ logger.warn(`\u9879\u76EE\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25: ${error}`);
1233
+ return null;
1234
+ }
1235
+ }
1236
+ function saveProjectConfig(config2) {
1237
+ const projectName = getProjectName();
1238
+ const configPath = getProjectConfigPath(projectName);
1239
+ const projectDir = join3(PROJECTS_CONFIG_DIR, projectName);
1240
+ ensureDir(projectDir);
1241
+ writeFileSync(configPath, JSON.stringify(config2, null, 2), "utf-8");
1242
+ logger.info(`\u9879\u76EE\u914D\u7F6E\u5DF2\u4FDD\u5B58: ${configPath}`);
1243
+ }
1244
+ function requireProjectConfig() {
1245
+ const config2 = loadProjectConfig();
1246
+ if (!config2) {
1247
+ throw new ClawtError(MESSAGES.PROJECT_NOT_INITIALIZED);
1248
+ }
1249
+ if (!config2.clawtMainWorkBranch) {
1250
+ throw new ClawtError(MESSAGES.PROJECT_CONFIG_MISSING_BRANCH);
1251
+ }
1252
+ return config2;
1253
+ }
1254
+ function getMainWorkBranch() {
1255
+ const config2 = requireProjectConfig();
1256
+ return config2.clawtMainWorkBranch;
1257
+ }
1258
+
1259
+ // src/utils/validate-branch.ts
1260
+ function getValidateBranchName(branchName) {
1261
+ return `${VALIDATE_BRANCH_PREFIX}${branchName}`;
1262
+ }
1263
+ function createValidateBranch(branchName, cwd) {
1264
+ const validateBranchName = getValidateBranchName(branchName);
1265
+ if (checkBranchExists(validateBranchName, cwd)) {
1266
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA: ${validateBranchName}`);
1267
+ return;
1268
+ }
1269
+ createBranch(validateBranchName, cwd);
1270
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u5DF2\u521B\u5EFA: ${validateBranchName}`);
1271
+ }
1272
+ function deleteValidateBranch(branchName, cwd) {
1273
+ const validateBranchName = getValidateBranchName(branchName);
1274
+ if (!checkBranchExists(validateBranchName, cwd)) {
1275
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5220\u9664: ${validateBranchName}`);
1276
+ return;
1277
+ }
1278
+ deleteBranch(validateBranchName, cwd);
1279
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u5DF2\u5220\u9664: ${validateBranchName}`);
1280
+ }
1281
+ async function rebuildValidateBranch(branchName, cwd) {
1282
+ const mainBranch = getMainWorkBranch();
1283
+ const currentBranch = getCurrentBranch(cwd);
1284
+ if (currentBranch === mainBranch) {
1285
+ } else if (currentBranch.startsWith(VALIDATE_BRANCH_PREFIX)) {
1286
+ gitResetHard(cwd);
1287
+ gitCleanForce(cwd);
1288
+ gitCheckout(mainBranch, cwd);
1289
+ } else {
1290
+ if (!isWorkingDirClean(cwd)) {
1291
+ await handleDirtyWorkingDir(cwd);
1292
+ }
1293
+ gitCheckout(mainBranch, cwd);
1294
+ }
1295
+ deleteValidateBranch(branchName, cwd);
1296
+ createValidateBranch(branchName, cwd);
1297
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u5DF2\u91CD\u5EFA: ${getValidateBranchName(branchName)}`);
1298
+ }
1299
+ async function handleDirtyWorkingDir(cwd) {
1300
+ printWarning("\u5F53\u524D\u5206\u652F\u6709\u672A\u63D0\u4EA4\u7684\u66F4\u6539\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F\uFF1A\n");
1301
+ const choice = await new Enquirer.Select({
1302
+ message: "\u9009\u62E9\u5904\u7406\u65B9\u5F0F",
1303
+ choices: [
1304
+ {
1305
+ name: "reset",
1306
+ message: "reset - \u4E22\u5F03\u6240\u6709\u66F4\u6539 (git reset --hard HEAD && git clean -fd)"
1307
+ },
1308
+ {
1309
+ name: "stash",
1310
+ message: "stash - \u6682\u5B58\u66F4\u6539 (git add . && git stash)"
1311
+ },
1312
+ {
1313
+ name: "exit",
1314
+ message: "exit - \u9000\u51FA\uFF0C\u624B\u52A8\u5904\u7406"
1315
+ }
1316
+ ],
1317
+ initial: 0
1318
+ }).run();
1319
+ if (choice === "exit") {
1320
+ throw new ClawtError("\u7528\u6237\u9009\u62E9\u9000\u51FA\uFF0C\u8BF7\u624B\u52A8\u5904\u7406\u5DE5\u4F5C\u533A\u66F4\u6539\u540E\u91CD\u8BD5");
1321
+ }
1322
+ if (choice === "reset") {
1323
+ gitResetHard(cwd);
1324
+ gitCleanForce(cwd);
1325
+ } else if (choice === "stash") {
1326
+ gitAddAll(cwd);
1327
+ gitStashPush("clawt:auto-stash", cwd);
1328
+ }
1329
+ if (!isWorkingDirClean(cwd)) {
1330
+ throw new ClawtError("\u5DE5\u4F5C\u533A\u4ECD\u7136\u4E0D\u5E72\u51C0\uFF0C\u8BF7\u624B\u52A8\u5904\u7406");
1331
+ }
1332
+ }
1333
+ async function ensureOnMainWorkBranch(cwd) {
1334
+ const mainBranch = getMainWorkBranch();
1335
+ const currentBranch = getCurrentBranch(cwd);
1336
+ if (currentBranch === mainBranch) {
1337
+ return;
1338
+ }
1339
+ if (currentBranch.startsWith(VALIDATE_BRANCH_PREFIX)) {
1340
+ logger.info(`\u5F53\u524D\u5728\u9A8C\u8BC1\u5206\u652F ${currentBranch} \u4E0A\uFF0C\u81EA\u52A8\u5207\u56DE\u4E3B\u5DE5\u4F5C\u5206\u652F ${mainBranch}`);
1341
+ if (!isWorkingDirClean(cwd)) {
1342
+ gitResetHard(cwd);
1343
+ gitCleanForce(cwd);
1344
+ }
1345
+ gitCheckout(mainBranch, cwd);
1346
+ return;
1347
+ }
1348
+ logger.info(`\u5F53\u524D\u5728\u5206\u652F ${currentBranch} \u4E0A\uFF0C\u9700\u5207\u6362\u5230\u4E3B\u5DE5\u4F5C\u5206\u652F ${mainBranch}`);
1349
+ if (!isWorkingDirClean(cwd)) {
1350
+ await handleDirtyWorkingDir(cwd);
1351
+ }
1352
+ gitCheckout(mainBranch, cwd);
1353
+ }
1354
+
1121
1355
  // src/utils/worktree.ts
1122
1356
  function getProjectWorktreeDir() {
1123
1357
  const projectName = getProjectName();
1124
- return join3(WORKTREES_DIR, projectName);
1358
+ return join4(WORKTREES_DIR, projectName);
1125
1359
  }
1126
1360
  function createWorktrees(branchName, count) {
1127
1361
  const sanitized = sanitizeBranchName(branchName);
@@ -1131,8 +1365,9 @@ function createWorktrees(branchName, count) {
1131
1365
  ensureDir(projectDir);
1132
1366
  const results = [];
1133
1367
  for (const name of branchNames) {
1134
- const worktreePath = join3(projectDir, name);
1368
+ const worktreePath = join4(projectDir, name);
1135
1369
  createWorktree(name, worktreePath);
1370
+ createValidateBranch(name);
1136
1371
  results.push({ path: worktreePath, branch: name });
1137
1372
  logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
1138
1373
  }
@@ -1144,8 +1379,9 @@ function createWorktreesByBranches(branchNames) {
1144
1379
  ensureDir(projectDir);
1145
1380
  const results = [];
1146
1381
  for (const name of branchNames) {
1147
- const worktreePath = join3(projectDir, name);
1382
+ const worktreePath = join4(projectDir, name);
1148
1383
  createWorktree(name, worktreePath);
1384
+ createValidateBranch(name);
1149
1385
  results.push({ path: worktreePath, branch: name });
1150
1386
  logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
1151
1387
  }
@@ -1153,7 +1389,7 @@ function createWorktreesByBranches(branchNames) {
1153
1389
  }
1154
1390
  function getProjectWorktrees() {
1155
1391
  const projectDir = getProjectWorktreeDir();
1156
- if (!existsSync3(projectDir)) {
1392
+ if (!existsSync4(projectDir)) {
1157
1393
  return [];
1158
1394
  }
1159
1395
  const worktreeListOutput = gitWorktreeList();
@@ -1166,7 +1402,7 @@ function getProjectWorktrees() {
1166
1402
  if (!entry.isDirectory()) {
1167
1403
  continue;
1168
1404
  }
1169
- const fullPath = join3(projectDir, entry.name);
1405
+ const fullPath = join4(projectDir, entry.name);
1170
1406
  if (registeredPaths.has(fullPath)) {
1171
1407
  worktrees.push({
1172
1408
  path: fullPath,
@@ -1181,6 +1417,7 @@ function cleanupWorktrees(worktrees) {
1181
1417
  try {
1182
1418
  removeWorktreeByPath(wt.path);
1183
1419
  deleteBranch(wt.branch);
1420
+ deleteValidateBranch(wt.branch);
1184
1421
  logger.info(`\u5DF2\u6E05\u7406 worktree \u548C\u5206\u652F: ${wt.branch}`);
1185
1422
  } catch (error) {
1186
1423
  logger.error(`\u6E05\u7406 worktree \u5931\u8D25: ${wt.path} - ${error}`);
@@ -1203,13 +1440,13 @@ function getWorktreeStatus(worktree) {
1203
1440
  }
1204
1441
 
1205
1442
  // src/utils/config.ts
1206
- import { existsSync as existsSync4, readFileSync, writeFileSync } from "fs";
1443
+ import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1207
1444
  function loadConfig() {
1208
- if (!existsSync4(CONFIG_PATH)) {
1445
+ if (!existsSync5(CONFIG_PATH)) {
1209
1446
  return { ...DEFAULT_CONFIG };
1210
1447
  }
1211
1448
  try {
1212
- const raw = readFileSync(CONFIG_PATH, "utf-8");
1449
+ const raw = readFileSync2(CONFIG_PATH, "utf-8");
1213
1450
  return { ...DEFAULT_CONFIG, ...JSON.parse(raw) };
1214
1451
  } catch {
1215
1452
  logger.warn(MESSAGES.CONFIG_CORRUPTED);
@@ -1218,13 +1455,13 @@ function loadConfig() {
1218
1455
  }
1219
1456
  }
1220
1457
  function writeConfig(config2) {
1221
- writeFileSync(CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
1458
+ writeFileSync2(CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
1222
1459
  }
1223
1460
  function writeDefaultConfig() {
1224
1461
  writeConfig(DEFAULT_CONFIG);
1225
1462
  }
1226
1463
  function saveConfig(config2) {
1227
- writeFileSync(CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
1464
+ writeFileSync2(CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
1228
1465
  }
1229
1466
  function getConfigValue(key) {
1230
1467
  const config2 = loadConfig();
@@ -1234,6 +1471,7 @@ function ensureClawtDirs() {
1234
1471
  ensureDir(CLAWT_HOME);
1235
1472
  ensureDir(LOGS_DIR);
1236
1473
  ensureDir(WORKTREES_DIR);
1474
+ ensureDir(PROJECTS_CONFIG_DIR);
1237
1475
  }
1238
1476
  function parseConcurrency(optionValue, configValue) {
1239
1477
  if (optionValue === void 0) {
@@ -1247,18 +1485,18 @@ function parseConcurrency(optionValue, configValue) {
1247
1485
  }
1248
1486
 
1249
1487
  // src/utils/prompt.ts
1250
- import Enquirer from "enquirer";
1488
+ import Enquirer2 from "enquirer";
1251
1489
 
1252
1490
  // src/utils/claude.ts
1253
1491
  import { spawnSync as spawnSync2 } from "child_process";
1254
- import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
1255
- import { join as join4 } from "path";
1492
+ import { existsSync as existsSync7, readdirSync as readdirSync3 } from "fs";
1493
+ import { join as join5 } from "path";
1256
1494
 
1257
1495
  // src/utils/terminal.ts
1258
1496
  import { execFileSync as execFileSync2 } from "child_process";
1259
- import { existsSync as existsSync5 } from "fs";
1497
+ import { existsSync as existsSync6 } from "fs";
1260
1498
  function isITerm2Installed() {
1261
- return existsSync5(ITERM2_APP_PATH);
1499
+ return existsSync6(ITERM2_APP_PATH);
1262
1500
  }
1263
1501
  function detectTerminalApp() {
1264
1502
  const configured = getConfigValue("terminalApp");
@@ -1331,8 +1569,8 @@ function encodeClaudeProjectPath(absolutePath) {
1331
1569
  }
1332
1570
  function hasClaudeSessionHistory(worktreePath) {
1333
1571
  const encodedName = encodeClaudeProjectPath(worktreePath);
1334
- const projectDir = join4(CLAUDE_PROJECTS_DIR, encodedName);
1335
- if (!existsSync6(projectDir)) {
1572
+ const projectDir = join5(CLAUDE_PROJECTS_DIR, encodedName);
1573
+ if (!existsSync7(projectDir)) {
1336
1574
  return false;
1337
1575
  }
1338
1576
  const entries = readdirSync3(projectDir);
@@ -1388,20 +1626,20 @@ function launchInteractiveClaudeInNewTerminal(worktree, hasPreviousSession) {
1388
1626
  }
1389
1627
 
1390
1628
  // src/utils/validate-snapshot.ts
1391
- import { join as join5 } from "path";
1392
- import { existsSync as existsSync7, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync, readdirSync as readdirSync4, rmdirSync as rmdirSync2, statSync as statSync2 } from "fs";
1629
+ import { join as join6 } from "path";
1630
+ import { existsSync as existsSync8, readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync, readdirSync as readdirSync4, rmdirSync as rmdirSync2, statSync as statSync2 } from "fs";
1393
1631
  function getSnapshotPath(projectName, branchName) {
1394
- return join5(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
1632
+ return join6(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
1395
1633
  }
1396
1634
  function getSnapshotHeadPath(projectName, branchName) {
1397
- return join5(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
1635
+ return join6(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
1398
1636
  }
1399
1637
  function hasSnapshot(projectName, branchName) {
1400
- return existsSync7(getSnapshotPath(projectName, branchName));
1638
+ return existsSync8(getSnapshotPath(projectName, branchName));
1401
1639
  }
1402
1640
  function getSnapshotModifiedTime(projectName, branchName) {
1403
1641
  const snapshotPath = getSnapshotPath(projectName, branchName);
1404
- if (!existsSync7(snapshotPath)) return null;
1642
+ if (!existsSync8(snapshotPath)) return null;
1405
1643
  const stat = statSync2(snapshotPath);
1406
1644
  return stat.mtime.toISOString();
1407
1645
  }
@@ -1409,47 +1647,47 @@ function readSnapshot(projectName, branchName) {
1409
1647
  const snapshotPath = getSnapshotPath(projectName, branchName);
1410
1648
  const headPath = getSnapshotHeadPath(projectName, branchName);
1411
1649
  logger.debug(`\u8BFB\u53D6 validate \u5FEB\u7167: ${snapshotPath}`);
1412
- const treeHash = existsSync7(snapshotPath) ? readFileSync2(snapshotPath, "utf-8").trim() : "";
1413
- const headCommitHash = existsSync7(headPath) ? readFileSync2(headPath, "utf-8").trim() : "";
1650
+ const treeHash = existsSync8(snapshotPath) ? readFileSync3(snapshotPath, "utf-8").trim() : "";
1651
+ const headCommitHash = existsSync8(headPath) ? readFileSync3(headPath, "utf-8").trim() : "";
1414
1652
  return { treeHash, headCommitHash };
1415
1653
  }
1416
1654
  function writeSnapshot(projectName, branchName, treeHash, headCommitHash) {
1417
1655
  const snapshotPath = getSnapshotPath(projectName, branchName);
1418
1656
  const headPath = getSnapshotHeadPath(projectName, branchName);
1419
- const snapshotDir = join5(VALIDATE_SNAPSHOTS_DIR, projectName);
1657
+ const snapshotDir = join6(VALIDATE_SNAPSHOTS_DIR, projectName);
1420
1658
  ensureDir(snapshotDir);
1421
- writeFileSync2(snapshotPath, treeHash, "utf-8");
1422
- writeFileSync2(headPath, headCommitHash, "utf-8");
1659
+ writeFileSync3(snapshotPath, treeHash, "utf-8");
1660
+ writeFileSync3(headPath, headCommitHash, "utf-8");
1423
1661
  logger.info(`\u5DF2\u4FDD\u5B58 validate \u5FEB\u7167: ${snapshotPath}, ${headPath}`);
1424
1662
  }
1425
1663
  function removeSnapshot(projectName, branchName) {
1426
1664
  const snapshotPath = getSnapshotPath(projectName, branchName);
1427
1665
  const headPath = getSnapshotHeadPath(projectName, branchName);
1428
- if (existsSync7(snapshotPath)) {
1666
+ if (existsSync8(snapshotPath)) {
1429
1667
  unlinkSync(snapshotPath);
1430
1668
  logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${snapshotPath}`);
1431
1669
  }
1432
- if (existsSync7(headPath)) {
1670
+ if (existsSync8(headPath)) {
1433
1671
  unlinkSync(headPath);
1434
1672
  logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${headPath}`);
1435
1673
  }
1436
1674
  }
1437
1675
  function getProjectSnapshotBranches(projectName) {
1438
- const projectDir = join5(VALIDATE_SNAPSHOTS_DIR, projectName);
1439
- if (!existsSync7(projectDir)) {
1676
+ const projectDir = join6(VALIDATE_SNAPSHOTS_DIR, projectName);
1677
+ if (!existsSync8(projectDir)) {
1440
1678
  return [];
1441
1679
  }
1442
1680
  const files = readdirSync4(projectDir);
1443
1681
  return files.filter((f) => f.endsWith(".tree")).map((f) => f.replace(/\.tree$/, ""));
1444
1682
  }
1445
1683
  function removeProjectSnapshots(projectName) {
1446
- const projectDir = join5(VALIDATE_SNAPSHOTS_DIR, projectName);
1447
- if (!existsSync7(projectDir)) {
1684
+ const projectDir = join6(VALIDATE_SNAPSHOTS_DIR, projectName);
1685
+ if (!existsSync8(projectDir)) {
1448
1686
  return;
1449
1687
  }
1450
1688
  const files = readdirSync4(projectDir);
1451
1689
  for (const file of files) {
1452
- unlinkSync(join5(projectDir, file));
1690
+ unlinkSync(join6(projectDir, file));
1453
1691
  }
1454
1692
  try {
1455
1693
  rmdirSync2(projectDir);
@@ -1459,7 +1697,7 @@ function removeProjectSnapshots(projectName) {
1459
1697
  }
1460
1698
 
1461
1699
  // src/utils/worktree-matcher.ts
1462
- import Enquirer2 from "enquirer";
1700
+ import Enquirer3 from "enquirer";
1463
1701
  import { statSync as statSync3 } from "fs";
1464
1702
  function findExactMatch(worktrees, branchName) {
1465
1703
  return worktrees.find((wt) => wt.branch === branchName);
@@ -1469,7 +1707,7 @@ function findFuzzyMatches(worktrees, keyword) {
1469
1707
  return worktrees.filter((wt) => wt.branch.toLowerCase().includes(lowerKeyword));
1470
1708
  }
1471
1709
  async function promptSelectBranch(worktrees, message) {
1472
- const selectedBranch = await new Enquirer2.Select({
1710
+ const selectedBranch = await new Enquirer3.Select({
1473
1711
  message,
1474
1712
  choices: worktrees.map((wt) => ({
1475
1713
  name: wt.branch,
@@ -1487,7 +1725,7 @@ async function promptMultiSelectBranches(worktrees, message) {
1487
1725
  { name: SELECT_ALL_NAME, message: SELECT_ALL_LABEL },
1488
1726
  ...branchChoices
1489
1727
  ];
1490
- const MultiSelect = Enquirer2.MultiSelect;
1728
+ const MultiSelect = Enquirer3.MultiSelect;
1491
1729
  class MultiSelectWithSelectAll extends MultiSelect {
1492
1730
  space() {
1493
1731
  if (!this.focused) return;
@@ -1642,7 +1880,7 @@ async function promptGroupedMultiSelectBranches(worktrees, message) {
1642
1880
  const groupMembershipMap = buildGroupMembershipMap(groups);
1643
1881
  const groupSelectAllNames = new Set(groupMembershipMap.keys());
1644
1882
  const allBranchNames = new Set(worktrees.map((wt) => wt.branch));
1645
- const MultiSelect = Enquirer2.MultiSelect;
1883
+ const MultiSelect = Enquirer3.MultiSelect;
1646
1884
  class MultiSelectWithGroupSelectAll extends MultiSelect {
1647
1885
  space() {
1648
1886
  if (!this.focused) return;
@@ -1700,7 +1938,7 @@ async function promptGroupedMultiSelectBranches(worktrees, message) {
1700
1938
  }
1701
1939
 
1702
1940
  // src/utils/progress-render.ts
1703
- import chalk4 from "chalk";
1941
+ import chalk5 from "chalk";
1704
1942
  import stringWidth from "string-width";
1705
1943
  var ANSI_RESET = "\x1B[0m";
1706
1944
  function truncateToTerminalWidth(text, maxWidth) {
@@ -1737,23 +1975,23 @@ function renderTaskLine(task, total, maxPathWidth, spinnerChar) {
1737
1975
  const pathStr = task.path.padEnd(maxPathWidth);
1738
1976
  switch (task.status) {
1739
1977
  case "pending": {
1740
- return `${indexStr} ${pathStr} ${chalk4.gray(TASK_STATUS_ICONS.PENDING)} ${chalk4.gray(TASK_STATUS_LABELS.PENDING)}`;
1978
+ return `${indexStr} ${pathStr} ${chalk5.gray(TASK_STATUS_ICONS.PENDING)} ${chalk5.gray(TASK_STATUS_LABELS.PENDING)}`;
1741
1979
  }
1742
1980
  case "running": {
1743
1981
  const elapsed = formatDuration(Date.now() - task.startedAt);
1744
- const detail = task.activity ? ` ${chalk4.dim(task.activity)}` : "";
1745
- return `${indexStr} ${pathStr} ${chalk4.cyan(spinnerChar)} ${chalk4.cyan(TASK_STATUS_LABELS.RUNNING)} ${chalk4.gray(elapsed)}${detail}`;
1982
+ const detail = task.activity ? ` ${chalk5.dim(task.activity)}` : "";
1983
+ return `${indexStr} ${pathStr} ${chalk5.cyan(spinnerChar)} ${chalk5.cyan(TASK_STATUS_LABELS.RUNNING)} ${chalk5.gray(elapsed)}${detail}`;
1746
1984
  }
1747
1985
  case "done": {
1748
1986
  const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
1749
1987
  const cost = task.costUsd != null ? `$${task.costUsd.toFixed(2)}` : "";
1750
- const preview = task.resultPreview ? ` ${chalk4.dim(task.resultPreview)}` : "";
1751
- return `${indexStr} ${pathStr} ${chalk4.green(TASK_STATUS_ICONS.DONE)} ${chalk4.green(TASK_STATUS_LABELS.DONE)} ${chalk4.gray(duration)} ${chalk4.yellow(cost)}${preview}`;
1988
+ const preview = task.resultPreview ? ` ${chalk5.dim(task.resultPreview)}` : "";
1989
+ return `${indexStr} ${pathStr} ${chalk5.green(TASK_STATUS_ICONS.DONE)} ${chalk5.green(TASK_STATUS_LABELS.DONE)} ${chalk5.gray(duration)} ${chalk5.yellow(cost)}${preview}`;
1752
1990
  }
1753
1991
  case "failed": {
1754
1992
  const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
1755
- const preview = task.resultPreview ? ` ${chalk4.dim(task.resultPreview)}` : "";
1756
- return `${indexStr} ${pathStr} ${chalk4.red(TASK_STATUS_ICONS.FAILED)} ${chalk4.red(TASK_STATUS_LABELS.FAILED)} ${chalk4.gray(duration)}${preview}`;
1993
+ const preview = task.resultPreview ? ` ${chalk5.dim(task.resultPreview)}` : "";
1994
+ return `${indexStr} ${pathStr} ${chalk5.red(TASK_STATUS_ICONS.FAILED)} ${chalk5.red(TASK_STATUS_LABELS.FAILED)} ${chalk5.gray(duration)}${preview}`;
1757
1995
  }
1758
1996
  }
1759
1997
  }
@@ -1763,10 +2001,10 @@ function renderSummaryLine(tasks, total) {
1763
2001
  const failed = tasks.filter((t) => t.status === "failed").length;
1764
2002
  const pending = tasks.filter((t) => t.status === "pending").length;
1765
2003
  const parts = [];
1766
- if (running > 0) parts.push(chalk4.cyan(`${running}/${total} ${TASK_STATUS_LABELS.RUNNING}`));
1767
- if (done > 0) parts.push(chalk4.green(`${done}/${total} ${TASK_STATUS_LABELS.DONE}`));
1768
- if (failed > 0) parts.push(chalk4.red(`${failed}/${total} ${TASK_STATUS_LABELS.FAILED}`));
1769
- if (pending > 0) parts.push(chalk4.gray(`${pending}/${total} ${TASK_STATUS_LABELS.PENDING}`));
2004
+ if (running > 0) parts.push(chalk5.cyan(`${running}/${total} ${TASK_STATUS_LABELS.RUNNING}`));
2005
+ if (done > 0) parts.push(chalk5.green(`${done}/${total} ${TASK_STATUS_LABELS.DONE}`));
2006
+ if (failed > 0) parts.push(chalk5.red(`${failed}/${total} ${TASK_STATUS_LABELS.FAILED}`));
2007
+ if (pending > 0) parts.push(chalk5.gray(`${pending}/${total} ${TASK_STATUS_LABELS.PENDING}`));
1770
2008
  return `[${parts.join(", ")}]`;
1771
2009
  }
1772
2010
 
@@ -1998,7 +2236,7 @@ var ProgressRenderer = class {
1998
2236
 
1999
2237
  // src/utils/task-file.ts
2000
2238
  import { resolve } from "path";
2001
- import { existsSync as existsSync8, readFileSync as readFileSync3 } from "fs";
2239
+ import { existsSync as existsSync9, readFileSync as readFileSync4 } from "fs";
2002
2240
  var TASK_BLOCK_REGEX = /<!-- CLAWT-TASKS:START -->([\s\S]*?)<!-- CLAWT-TASKS:END -->/g;
2003
2241
  var BRANCH_LINE_REGEX = /^#\s*branch:\s*(.+)$/;
2004
2242
  var EMPTY_TASKS_MESSAGE = "\u4EFB\u52A1\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A";
@@ -2044,10 +2282,10 @@ function parseTaskFile(content, options) {
2044
2282
  }
2045
2283
  function loadTaskFile(filePath, options) {
2046
2284
  const absolutePath = resolve(filePath);
2047
- if (!existsSync8(absolutePath)) {
2285
+ if (!existsSync9(absolutePath)) {
2048
2286
  throw new ClawtError(MESSAGES.TASK_FILE_NOT_FOUND(absolutePath));
2049
2287
  }
2050
- const content = readFileSync3(absolutePath, "utf-8");
2288
+ const content = readFileSync4(absolutePath, "utf-8");
2051
2289
  const entries = parseTaskFile(content, options);
2052
2290
  if (entries.length === 0) {
2053
2291
  throw new ClawtError(MESSAGES.TASK_FILE_EMPTY);
@@ -2381,8 +2619,8 @@ async function executeBatchTasks(worktrees, tasks, concurrency) {
2381
2619
  }
2382
2620
 
2383
2621
  // src/utils/dry-run.ts
2384
- import chalk5 from "chalk";
2385
- import { join as join6 } from "path";
2622
+ import chalk6 from "chalk";
2623
+ import { join as join7 } from "path";
2386
2624
  var DRY_RUN_TASK_DESC_MAX_LENGTH = 80;
2387
2625
  function truncateTaskDesc(task) {
2388
2626
  const oneLine = task.replace(/\n/g, " ").trim();
@@ -2395,7 +2633,7 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
2395
2633
  const projectDir = getProjectWorktreeDir();
2396
2634
  const isInteractive = tasks.length === 0;
2397
2635
  printDoubleSeparator();
2398
- printInfo(` ${chalk5.bold(MESSAGES.DRY_RUN_TITLE)}`);
2636
+ printInfo(` ${chalk6.bold(MESSAGES.DRY_RUN_TITLE)}`);
2399
2637
  printDoubleSeparator();
2400
2638
  const summaryParts = [
2401
2639
  MESSAGES.DRY_RUN_TASK_COUNT(branchNames.length),
@@ -2405,31 +2643,31 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
2405
2643
  if (isInteractive) {
2406
2644
  summaryParts.push(MESSAGES.DRY_RUN_INTERACTIVE_MODE);
2407
2645
  }
2408
- printInfo(summaryParts.join(chalk5.gray(" \u2502 ")));
2646
+ printInfo(summaryParts.join(chalk6.gray(" \u2502 ")));
2409
2647
  printSeparator();
2410
2648
  let hasConflict = false;
2411
2649
  for (let i = 0; i < branchNames.length; i++) {
2412
2650
  const branch = branchNames[i];
2413
- const worktreePath = join6(projectDir, branch);
2651
+ const worktreePath = join7(projectDir, branch);
2414
2652
  const exists = checkBranchExists(branch);
2415
2653
  if (exists) hasConflict = true;
2416
2654
  const indexLabel = `[${i + 1}/${branchNames.length}]`;
2417
2655
  if (exists) {
2418
- printInfo(`${chalk5.yellow("\u26A0")} ${indexLabel} ${chalk5.yellow(branch)} ${chalk5.gray("\u2014")} ${chalk5.yellow(MESSAGES.DRY_RUN_BRANCH_EXISTS_WARNING(branch))}`);
2656
+ printInfo(`${chalk6.yellow("\u26A0")} ${indexLabel} ${chalk6.yellow(branch)} ${chalk6.gray("\u2014")} ${chalk6.yellow(MESSAGES.DRY_RUN_BRANCH_EXISTS_WARNING(branch))}`);
2419
2657
  } else {
2420
- printInfo(`${chalk5.green("\u2713")} ${indexLabel} ${chalk5.cyan(branch)}`);
2658
+ printInfo(`${chalk6.green("\u2713")} ${indexLabel} ${chalk6.cyan(branch)}`);
2421
2659
  }
2422
- printInfo(` ${chalk5.gray("\u8DEF\u5F84:")} ${worktreePath}`);
2660
+ printInfo(` ${chalk6.gray("\u8DEF\u5F84:")} ${worktreePath}`);
2423
2661
  if (!isInteractive) {
2424
- printInfo(` ${chalk5.gray("\u4EFB\u52A1:")} ${truncateTaskDesc(tasks[i])}`);
2662
+ printInfo(` ${chalk6.gray("\u4EFB\u52A1:")} ${truncateTaskDesc(tasks[i])}`);
2425
2663
  }
2426
2664
  printInfo("");
2427
2665
  }
2428
2666
  printDoubleSeparator();
2429
2667
  if (hasConflict) {
2430
- printInfo(chalk5.yellow(`\u26A0 ${MESSAGES.DRY_RUN_HAS_CONFLICT}`));
2668
+ printInfo(chalk6.yellow(`\u26A0 ${MESSAGES.DRY_RUN_HAS_CONFLICT}`));
2431
2669
  } else {
2432
- printInfo(chalk5.green(`\u2713 ${MESSAGES.DRY_RUN_READY}`));
2670
+ printInfo(chalk6.green(`\u2713 ${MESSAGES.DRY_RUN_READY}`));
2433
2671
  }
2434
2672
  }
2435
2673
 
@@ -2447,8 +2685,8 @@ function applyAliases(program2, aliases) {
2447
2685
  }
2448
2686
 
2449
2687
  // src/utils/config-strategy.ts
2450
- import chalk6 from "chalk";
2451
- import Enquirer3 from "enquirer";
2688
+ import chalk7 from "chalk";
2689
+ import Enquirer4 from "enquirer";
2452
2690
  function isValidConfigKey(key) {
2453
2691
  return key in DEFAULT_CONFIG;
2454
2692
  }
@@ -2491,16 +2729,16 @@ async function promptConfigValue(key, currentValue) {
2491
2729
  }
2492
2730
  function formatConfigValue(value) {
2493
2731
  if (typeof value === "boolean") {
2494
- return value ? chalk6.green("true") : chalk6.yellow("false");
2732
+ return value ? chalk7.green("true") : chalk7.yellow("false");
2495
2733
  }
2496
- return chalk6.cyan(String(value));
2734
+ return chalk7.cyan(String(value));
2497
2735
  }
2498
2736
  async function promptBooleanValue(key, currentValue) {
2499
2737
  const choices = [
2500
2738
  { name: "true", message: "true" },
2501
2739
  { name: "false", message: "false" }
2502
2740
  ];
2503
- const selected = await new Enquirer3.Select({
2741
+ const selected = await new Enquirer4.Select({
2504
2742
  message: MESSAGES.CONFIG_INPUT_PROMPT(key),
2505
2743
  choices,
2506
2744
  initial: currentValue ? 0 : 1
@@ -2508,7 +2746,7 @@ async function promptBooleanValue(key, currentValue) {
2508
2746
  return selected === "true";
2509
2747
  }
2510
2748
  async function promptNumberValue(key, currentValue) {
2511
- const input = await new Enquirer3.Input({
2749
+ const input = await new Enquirer4.Input({
2512
2750
  message: MESSAGES.CONFIG_INPUT_PROMPT(key),
2513
2751
  initial: String(currentValue),
2514
2752
  validate: (val) => {
@@ -2523,28 +2761,28 @@ async function promptEnumValue(key, currentValue, allowedValues) {
2523
2761
  name: v,
2524
2762
  message: v
2525
2763
  }));
2526
- return await new Enquirer3.Select({
2764
+ return await new Enquirer4.Select({
2527
2765
  message: MESSAGES.CONFIG_INPUT_PROMPT(key),
2528
2766
  choices,
2529
2767
  initial: allowedValues.indexOf(currentValue)
2530
2768
  }).run();
2531
2769
  }
2532
2770
  async function promptStringValue(key, currentValue) {
2533
- return await new Enquirer3.Input({
2771
+ return await new Enquirer4.Input({
2534
2772
  message: MESSAGES.CONFIG_INPUT_PROMPT(key),
2535
2773
  initial: currentValue
2536
2774
  }).run();
2537
2775
  }
2538
2776
 
2539
2777
  // src/utils/update-checker.ts
2540
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2778
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
2541
2779
  import { execSync as execSync3 } from "child_process";
2542
2780
  import { request } from "https";
2543
- import chalk7 from "chalk";
2781
+ import chalk8 from "chalk";
2544
2782
  import stringWidth2 from "string-width";
2545
2783
  function readUpdateCache() {
2546
2784
  try {
2547
- const raw = readFileSync4(UPDATE_CHECK_PATH, "utf-8");
2785
+ const raw = readFileSync5(UPDATE_CHECK_PATH, "utf-8");
2548
2786
  return JSON.parse(raw);
2549
2787
  } catch {
2550
2788
  return null;
@@ -2552,7 +2790,7 @@ function readUpdateCache() {
2552
2790
  }
2553
2791
  function writeUpdateCache(cache) {
2554
2792
  try {
2555
- writeFileSync3(UPDATE_CHECK_PATH, JSON.stringify(cache, null, 2), "utf-8");
2793
+ writeFileSync4(UPDATE_CHECK_PATH, JSON.stringify(cache, null, 2), "utf-8");
2556
2794
  } catch {
2557
2795
  }
2558
2796
  }
@@ -2615,12 +2853,12 @@ function detectPackageManager() {
2615
2853
  }
2616
2854
  function printUpdateNotification(currentVersion, latestVersion) {
2617
2855
  const updateText = UPDATE_MESSAGES.UPDATE_AVAILABLE(
2618
- chalk7.red(currentVersion),
2619
- chalk7.green(latestVersion)
2856
+ chalk8.red(currentVersion),
2857
+ chalk8.green(latestVersion)
2620
2858
  );
2621
2859
  const pm = detectPackageManager();
2622
2860
  const updateCommand = UPDATE_COMMANDS[pm] || UPDATE_COMMANDS.npm;
2623
- const commandText = UPDATE_MESSAGES.UPDATE_HINT(chalk7.cyan(updateCommand));
2861
+ const commandText = UPDATE_MESSAGES.UPDATE_HINT(chalk8.cyan(updateCommand));
2624
2862
  const updateTextWidth = stringWidth2(updateText);
2625
2863
  const commandTextWidth = stringWidth2(commandText);
2626
2864
  const innerWidth = Math.max(updateTextWidth, commandTextWidth) + 4;
@@ -2667,10 +2905,642 @@ async function checkForUpdates(currentVersion) {
2667
2905
  }
2668
2906
  }
2669
2907
 
2908
+ // src/utils/json.ts
2909
+ function primitiveToString(value) {
2910
+ if (value === void 0) {
2911
+ return "undefined";
2912
+ }
2913
+ if (value === null) {
2914
+ return "null";
2915
+ }
2916
+ if (typeof value === "symbol") {
2917
+ return value.toString();
2918
+ }
2919
+ if (typeof value === "function") {
2920
+ return `[Function: ${value.name || "anonymous"}]`;
2921
+ }
2922
+ return String(value);
2923
+ }
2924
+ function safeStringify(value, indent = 2) {
2925
+ if (value === null || typeof value !== "object") {
2926
+ return primitiveToString(value);
2927
+ }
2928
+ try {
2929
+ const seen = /* @__PURE__ */ new WeakSet();
2930
+ return JSON.stringify(
2931
+ value,
2932
+ (_key, val) => {
2933
+ if (typeof val === "bigint") {
2934
+ return val.toString();
2935
+ }
2936
+ if (typeof val === "undefined" || typeof val === "function" || typeof val === "symbol") {
2937
+ return primitiveToString(val);
2938
+ }
2939
+ if (typeof val === "object" && val !== null) {
2940
+ if (seen.has(val)) {
2941
+ return "[Circular]";
2942
+ }
2943
+ seen.add(val);
2944
+ }
2945
+ return val;
2946
+ },
2947
+ indent
2948
+ );
2949
+ } catch {
2950
+ try {
2951
+ return JSON.stringify(String(value), null, indent);
2952
+ } catch {
2953
+ return "[Unserializable]";
2954
+ }
2955
+ }
2956
+ }
2957
+
2958
+ // src/utils/interactive-panel.ts
2959
+ import { createInterface as createInterface2 } from "readline";
2960
+
2961
+ // src/utils/interactive-panel-render.ts
2962
+ import chalk9 from "chalk";
2963
+ import stringWidth3 from "string-width";
2964
+ function buildSeparatorWithHint(cols, hint) {
2965
+ const maxWidth = Math.min(cols, 60);
2966
+ if (!hint) {
2967
+ return chalk9.gray("\u2500".repeat(maxWidth));
2968
+ }
2969
+ const hintWidth = stringWidth3(hint);
2970
+ const remaining = Math.max(maxWidth - 2 - hintWidth, 0);
2971
+ const leftLen = Math.floor(remaining / 2);
2972
+ const rightLen = remaining - leftLen;
2973
+ return `${chalk9.gray("\u2500".repeat(leftLen))} ${hint} ${chalk9.gray("\u2500".repeat(rightLen))}`;
2974
+ }
2975
+ function buildPanelFrame(statusResult, selectedIndex, scrollOffset, rows, cols, countdown) {
2976
+ const lines = [];
2977
+ lines.push(PANEL_TITLE(statusResult.main.projectName));
2978
+ lines.push(renderSnapshotSummary(statusResult.snapshots.total, statusResult.snapshots.orphaned));
2979
+ const visibleRows = calculateVisibleRows(rows);
2980
+ if (statusResult.worktrees.length === 0) {
2981
+ lines.push(buildSeparatorWithHint(cols, ""));
2982
+ lines.push(PANEL_NO_WORKTREES);
2983
+ lines.push(buildSeparatorWithHint(cols, ""));
2984
+ } else {
2985
+ const panelLines = buildGroupedWorktreeLines(statusResult.worktrees, selectedIndex);
2986
+ const hasOverflowUp = scrollOffset > 0;
2987
+ const hasOverflowDown = scrollOffset + visibleRows < panelLines.length;
2988
+ lines.push(buildSeparatorWithHint(cols, hasOverflowUp ? PANEL_OVERFLOW_UP_HINT : ""));
2989
+ const visibleLines = panelLines.slice(scrollOffset, scrollOffset + visibleRows);
2990
+ for (const pl of visibleLines) {
2991
+ lines.push(pl.text);
2992
+ }
2993
+ lines.push(buildSeparatorWithHint(cols, hasOverflowDown ? PANEL_OVERFLOW_DOWN_HINT : ""));
2994
+ }
2995
+ lines.push(renderFooter(countdown));
2996
+ return lines;
2997
+ }
2998
+ function buildDisplayOrder(worktrees) {
2999
+ const worktreeInfos = worktrees.map((wt) => ({ path: wt.path, branch: wt.branch }));
3000
+ const groups = groupWorktreesByDate(worktreeInfos);
3001
+ const branchIndexMap = /* @__PURE__ */ new Map();
3002
+ for (let i = 0; i < worktrees.length; i++) {
3003
+ branchIndexMap.set(worktrees[i].branch, i);
3004
+ }
3005
+ const displayOrder = [];
3006
+ for (const [, groupWorktrees] of groups) {
3007
+ for (const gwt of groupWorktrees) {
3008
+ displayOrder.push(branchIndexMap.get(gwt.branch));
3009
+ }
3010
+ }
3011
+ return displayOrder;
3012
+ }
3013
+ function buildGroupedWorktreeLines(worktrees, selectedIndex) {
3014
+ const panelLines = [];
3015
+ const worktreeInfos = worktrees.map((wt) => ({ path: wt.path, branch: wt.branch }));
3016
+ const groups = groupWorktreesByDate(worktreeInfos);
3017
+ const branchIndexMap = /* @__PURE__ */ new Map();
3018
+ for (let i = 0; i < worktrees.length; i++) {
3019
+ branchIndexMap.set(worktrees[i].branch, i);
3020
+ }
3021
+ for (const [dateKey, groupWorktrees] of groups) {
3022
+ panelLines.push({
3023
+ type: "separator",
3024
+ text: renderDateSeparator(dateKey)
3025
+ });
3026
+ panelLines.push({
3027
+ type: "separator",
3028
+ text: ""
3029
+ });
3030
+ for (const gwt of groupWorktrees) {
3031
+ const wtIndex = branchIndexMap.get(gwt.branch);
3032
+ const wt = worktrees[wtIndex];
3033
+ const isSelected = wtIndex === selectedIndex;
3034
+ const blockLines = renderWorktreeBlock(wt, isSelected);
3035
+ for (const line of blockLines) {
3036
+ panelLines.push({
3037
+ type: "worktree-content",
3038
+ text: line,
3039
+ worktreeIndex: wtIndex
3040
+ });
3041
+ }
3042
+ }
3043
+ }
3044
+ return panelLines;
3045
+ }
3046
+ function renderDateSeparator(dateKey) {
3047
+ const leftPad = " ";
3048
+ if (dateKey === UNKNOWN_DATE_GROUP) {
3049
+ return `${leftPad}${chalk9.gray(PANEL_DATE_SEPARATOR_PREFIX)} ${chalk9.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} ${chalk9.gray(PANEL_DATE_SEPARATOR_PREFIX)}`;
3050
+ }
3051
+ const relativeDate = formatRelativeDate(dateKey);
3052
+ return `${leftPad}${chalk9.gray(PANEL_DATE_SEPARATOR_PREFIX)} ${chalk9.bold.hex("#FF8C00")(dateKey)}${chalk9.hex("#FF8C00")(`\uFF08${relativeDate}\uFF09`)} ${chalk9.gray(PANEL_DATE_SEPARATOR_PREFIX)}`;
3053
+ }
3054
+ function renderWorktreeBlock(wt, isSelected) {
3055
+ const lines = [];
3056
+ const indicator = isSelected ? chalk9.cyan(SELECTED_INDICATOR) : UNSELECTED_INDICATOR;
3057
+ const branchStyle = isSelected ? chalk9.bold.cyan : chalk9.bold;
3058
+ const statusLabel = formatChangeStatusLabel(wt.changeStatus);
3059
+ lines.push(`${indicator} ${branchStyle(wt.branch)} [${statusLabel}]`);
3060
+ const indent = " ";
3061
+ if (wt.insertions > 0 || wt.deletions > 0) {
3062
+ lines.push(`${indent}${chalk9.green(`+${wt.insertions}`)} ${chalk9.red(`-${wt.deletions}`)}`);
3063
+ }
3064
+ if (wt.commitsAhead > 0) {
3065
+ lines.push(`${indent}${chalk9.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
3066
+ }
3067
+ if (wt.commitsBehind > 0) {
3068
+ lines.push(`${indent}${chalk9.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
3069
+ } else {
3070
+ lines.push(`${indent}${chalk9.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
3071
+ }
3072
+ if (wt.createdAt) {
3073
+ const relativeTime = formatRelativeTime(wt.createdAt);
3074
+ if (relativeTime) {
3075
+ lines.push(`${indent}${chalk9.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
3076
+ }
3077
+ }
3078
+ if (wt.snapshotTime) {
3079
+ const relativeTime = formatRelativeTime(wt.snapshotTime);
3080
+ if (relativeTime) {
3081
+ lines.push(`${indent}${chalk9.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
3082
+ }
3083
+ } else {
3084
+ lines.push(`${indent}${chalk9.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
3085
+ }
3086
+ lines.push("");
3087
+ return lines;
3088
+ }
3089
+ function formatChangeStatusLabel(status) {
3090
+ switch (status) {
3091
+ case "committed":
3092
+ return chalk9.green(MESSAGES.STATUS_CHANGE_COMMITTED);
3093
+ case "uncommitted":
3094
+ return chalk9.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
3095
+ case "conflict":
3096
+ return chalk9.red(MESSAGES.STATUS_CHANGE_CONFLICT);
3097
+ case "clean":
3098
+ return chalk9.gray(MESSAGES.STATUS_CHANGE_CLEAN);
3099
+ }
3100
+ }
3101
+ function renderSnapshotSummary(total, orphaned) {
3102
+ return PANEL_SNAPSHOT_SUMMARY(total, orphaned);
3103
+ }
3104
+ function renderFooter(countdown) {
3105
+ return `${PANEL_FOOTER_SHORTCUTS} ${PANEL_FOOTER_COUNTDOWN(countdown)}`;
3106
+ }
3107
+ function calculateVisibleRows(terminalRows) {
3108
+ const fixedRows = PANEL_FIXED_ROWS + 1;
3109
+ return Math.max(terminalRows - fixedRows, 3);
3110
+ }
3111
+
3112
+ // src/utils/interactive-panel.ts
3113
+ var InteractivePanel = class {
3114
+ /** 当前状态数据 */
3115
+ statusResult;
3116
+ /** 当前选中的显示位置索引(对应 displayOrder 数组的下标) */
3117
+ selectedDisplayIndex;
3118
+ /** 显示顺序到原始索引的映射(按日期分组后的排列顺序) */
3119
+ displayOrder;
3120
+ /** 滚动偏移(基于行数) */
3121
+ scrollOffset;
3122
+ /** 数据刷新定时器引用 */
3123
+ refreshTimer;
3124
+ /** 倒计时定时器引用 */
3125
+ countdownTimer;
3126
+ /** 刷新倒计时剩余秒数 */
3127
+ refreshCountdown;
3128
+ /** 是否已停止 */
3129
+ stopped;
3130
+ /** 是否为 TTY 环境 */
3131
+ isTTY;
3132
+ /** resize 事件处理器引用 */
3133
+ resizeHandler;
3134
+ /** exit 兜底处理器 */
3135
+ exitHandler;
3136
+ /** stdin 数据处理器引用(用于清理) */
3137
+ stdinDataHandler;
3138
+ /** 操作锁(防止操作期间响应按键) */
3139
+ isOperating;
3140
+ /** Promise resolve 函数(stop 时调用以完成 start 返回的 Promise) */
3141
+ resolveStart;
3142
+ /** 数据收集函数引用 */
3143
+ collectStatusFn;
3144
+ /**
3145
+ * 创建交互式面板
3146
+ * @param {() => StatusResult} collectStatusFn - 数据收集函数
3147
+ */
3148
+ constructor(collectStatusFn) {
3149
+ this.statusResult = null;
3150
+ this.selectedDisplayIndex = 0;
3151
+ this.displayOrder = [];
3152
+ this.scrollOffset = 0;
3153
+ this.refreshTimer = null;
3154
+ this.countdownTimer = null;
3155
+ this.refreshCountdown = PANEL_REFRESH_INTERVAL_MS / 1e3;
3156
+ this.stopped = false;
3157
+ this.isTTY = !!process.stdout.isTTY;
3158
+ this.resizeHandler = null;
3159
+ this.exitHandler = null;
3160
+ this.stdinDataHandler = null;
3161
+ this.isOperating = false;
3162
+ this.resolveStart = null;
3163
+ this.collectStatusFn = collectStatusFn;
3164
+ }
3165
+ /**
3166
+ * 启动交互式面板
3167
+ * 非 TTY 时打印提示并退出
3168
+ * @returns {Promise<void>} 面板关闭时 resolve
3169
+ */
3170
+ start() {
3171
+ if (!this.isTTY) {
3172
+ console.log(PANEL_NOT_TTY);
3173
+ return Promise.resolve();
3174
+ }
3175
+ return new Promise((resolve3) => {
3176
+ this.resolveStart = resolve3;
3177
+ this.statusResult = this.collectStatusFn();
3178
+ this.displayOrder = buildDisplayOrder(this.statusResult.worktrees);
3179
+ this.initTerminal();
3180
+ this.startKeyboardListener();
3181
+ this.startAutoRefresh();
3182
+ this.render();
3183
+ });
3184
+ }
3185
+ /**
3186
+ * 停止面板,恢复终端状态
3187
+ */
3188
+ stop() {
3189
+ if (this.stopped) return;
3190
+ this.stopped = true;
3191
+ this.clearTimers();
3192
+ this.stopKeyboardListener();
3193
+ this.restoreTerminal();
3194
+ if (this.resizeHandler) {
3195
+ process.stdout.removeListener("resize", this.resizeHandler);
3196
+ this.resizeHandler = null;
3197
+ }
3198
+ if (this.exitHandler) {
3199
+ process.removeListener("exit", this.exitHandler);
3200
+ this.exitHandler = null;
3201
+ }
3202
+ if (this.resolveStart) {
3203
+ this.resolveStart();
3204
+ this.resolveStart = null;
3205
+ }
3206
+ }
3207
+ /**
3208
+ * 初始化终端:进入备选屏幕、隐藏光标、禁用行换行
3209
+ */
3210
+ initTerminal() {
3211
+ process.stdout.write(ALT_SCREEN_ENTER);
3212
+ process.stdout.write(CURSOR_HIDE);
3213
+ process.stdout.write(LINE_WRAP_DISABLE);
3214
+ this.resizeHandler = () => {
3215
+ if (!this.stopped && !this.isOperating) {
3216
+ this.render();
3217
+ }
3218
+ };
3219
+ process.stdout.on("resize", this.resizeHandler);
3220
+ this.exitHandler = () => {
3221
+ process.stdout.write(LINE_WRAP_ENABLE);
3222
+ process.stdout.write(CURSOR_SHOW);
3223
+ process.stdout.write(ALT_SCREEN_LEAVE);
3224
+ };
3225
+ process.on("exit", this.exitHandler);
3226
+ }
3227
+ /**
3228
+ * 恢复终端:启用行换行、显示光标、退出备选屏幕
3229
+ */
3230
+ restoreTerminal() {
3231
+ process.stdout.write(LINE_WRAP_ENABLE);
3232
+ process.stdout.write(CURSOR_SHOW);
3233
+ process.stdout.write(ALT_SCREEN_LEAVE);
3234
+ }
3235
+ /**
3236
+ * 启动键盘监听
3237
+ * 将 stdin 设为 raw 模式以捕获每个按键
3238
+ */
3239
+ startKeyboardListener() {
3240
+ if (process.stdin.isTTY) {
3241
+ process.stdin.setRawMode(true);
3242
+ }
3243
+ process.stdin.resume();
3244
+ this.stdinDataHandler = (data) => {
3245
+ this.handleKeypress(data);
3246
+ };
3247
+ process.stdin.on("data", this.stdinDataHandler);
3248
+ }
3249
+ /**
3250
+ * 停止键盘监听,恢复 stdin 状态
3251
+ */
3252
+ stopKeyboardListener() {
3253
+ if (this.stdinDataHandler) {
3254
+ process.stdin.removeListener("data", this.stdinDataHandler);
3255
+ this.stdinDataHandler = null;
3256
+ }
3257
+ if (process.stdin.isTTY) {
3258
+ process.stdin.setRawMode(false);
3259
+ }
3260
+ process.stdin.pause();
3261
+ }
3262
+ /**
3263
+ * 处理键盘输入
3264
+ * @param {Buffer} data - 按键数据
3265
+ */
3266
+ handleKeypress(data) {
3267
+ if (this.isOperating) return;
3268
+ const str = data.toString();
3269
+ if (data[0] === KEY_CTRL_C) {
3270
+ this.stop();
3271
+ return;
3272
+ }
3273
+ if (str === KEY_ARROW_UP) {
3274
+ this.navigateUp();
3275
+ return;
3276
+ }
3277
+ if (str === KEY_ARROW_DOWN) {
3278
+ this.navigateDown();
3279
+ return;
3280
+ }
3281
+ const key = str.toLowerCase();
3282
+ if (key === PANEL_SHORTCUT_KEYS.QUIT) {
3283
+ this.stop();
3284
+ return;
3285
+ }
3286
+ if (key === PANEL_SHORTCUT_KEYS.REFRESH) {
3287
+ this.refreshData();
3288
+ return;
3289
+ }
3290
+ if (key === PANEL_SHORTCUT_KEYS.VALIDATE) {
3291
+ this.executeOperation(() => this.handleValidate());
3292
+ return;
3293
+ }
3294
+ if (key === PANEL_SHORTCUT_KEYS.MERGE) {
3295
+ this.executeOperation(() => this.handleMerge());
3296
+ return;
3297
+ }
3298
+ if (key === PANEL_SHORTCUT_KEYS.DELETE) {
3299
+ this.executeOperation(() => this.handleDelete());
3300
+ return;
3301
+ }
3302
+ if (key === PANEL_SHORTCUT_KEYS.RESUME) {
3303
+ this.executeOperation(() => this.handleResume());
3304
+ return;
3305
+ }
3306
+ if (key === PANEL_SHORTCUT_KEYS.SYNC) {
3307
+ this.executeOperation(() => this.handleSync());
3308
+ return;
3309
+ }
3310
+ }
3311
+ /**
3312
+ * 向上导航,选中显示顺序中的上一个 worktree
3313
+ */
3314
+ navigateUp() {
3315
+ if (!this.statusResult || this.displayOrder.length === 0) return;
3316
+ if (this.selectedDisplayIndex > 0) {
3317
+ this.selectedDisplayIndex--;
3318
+ this.adjustScrollForSelection();
3319
+ this.render();
3320
+ }
3321
+ }
3322
+ /**
3323
+ * 向下导航,选中显示顺序中的下一个 worktree
3324
+ */
3325
+ navigateDown() {
3326
+ if (!this.statusResult || this.displayOrder.length === 0) return;
3327
+ if (this.selectedDisplayIndex < this.displayOrder.length - 1) {
3328
+ this.selectedDisplayIndex++;
3329
+ this.adjustScrollForSelection();
3330
+ this.render();
3331
+ }
3332
+ }
3333
+ /**
3334
+ * 获取当前选中的原始 worktree 索引
3335
+ * @returns {number} 原始 worktrees 数组中的索引
3336
+ */
3337
+ getSelectedOriginalIndex() {
3338
+ return this.displayOrder[this.selectedDisplayIndex];
3339
+ }
3340
+ /**
3341
+ * 调整滚动偏移以确保选中项在可见区域内
3342
+ */
3343
+ adjustScrollForSelection() {
3344
+ if (!this.statusResult || this.displayOrder.length === 0) return;
3345
+ const originalIndex = this.getSelectedOriginalIndex();
3346
+ const rows = process.stdout.rows || 24;
3347
+ const visibleRows = calculateVisibleRows(rows);
3348
+ const panelLines = buildGroupedWorktreeLines(this.statusResult.worktrees, originalIndex);
3349
+ let firstLine = -1;
3350
+ let lastLine = -1;
3351
+ for (let i = 0; i < panelLines.length; i++) {
3352
+ if (panelLines[i].worktreeIndex === originalIndex) {
3353
+ if (firstLine === -1) firstLine = i;
3354
+ lastLine = i;
3355
+ }
3356
+ }
3357
+ if (firstLine === -1) return;
3358
+ let groupStart = firstLine;
3359
+ while (groupStart > 0 && panelLines[groupStart - 1].type === "separator") {
3360
+ groupStart--;
3361
+ }
3362
+ if (groupStart < this.scrollOffset) {
3363
+ this.scrollOffset = groupStart;
3364
+ }
3365
+ if (lastLine >= this.scrollOffset + visibleRows) {
3366
+ this.scrollOffset = lastLine - visibleRows + 1;
3367
+ }
3368
+ if (this.scrollOffset > groupStart) {
3369
+ this.scrollOffset = groupStart;
3370
+ }
3371
+ }
3372
+ /**
3373
+ * 启动自动刷新:数据刷新定时器 + 倒计时定时器
3374
+ */
3375
+ startAutoRefresh() {
3376
+ this.refreshCountdown = PANEL_REFRESH_INTERVAL_MS / 1e3;
3377
+ this.refreshTimer = setInterval(() => {
3378
+ this.refreshData();
3379
+ }, PANEL_REFRESH_INTERVAL_MS);
3380
+ this.countdownTimer = setInterval(() => {
3381
+ if (this.refreshCountdown > 0) {
3382
+ this.refreshCountdown--;
3383
+ }
3384
+ this.render();
3385
+ }, PANEL_COUNTDOWN_INTERVAL_MS);
3386
+ if (this.refreshTimer.unref) this.refreshTimer.unref();
3387
+ if (this.countdownTimer.unref) this.countdownTimer.unref();
3388
+ }
3389
+ /**
3390
+ * 清除所有定时器
3391
+ */
3392
+ clearTimers() {
3393
+ if (this.refreshTimer) {
3394
+ clearInterval(this.refreshTimer);
3395
+ this.refreshTimer = null;
3396
+ }
3397
+ if (this.countdownTimer) {
3398
+ clearInterval(this.countdownTimer);
3399
+ this.countdownTimer = null;
3400
+ }
3401
+ }
3402
+ /**
3403
+ * 刷新数据:记录当前选中分支 → 重新收集 → 恢复选中位置 → 重置倒计时 → 重绘
3404
+ */
3405
+ refreshData() {
3406
+ if (this.stopped || this.isOperating) return;
3407
+ const originalIndex = this.displayOrder[this.selectedDisplayIndex];
3408
+ const previousBranch = this.statusResult?.worktrees[originalIndex]?.branch;
3409
+ this.statusResult = this.collectStatusFn();
3410
+ this.displayOrder = buildDisplayOrder(this.statusResult.worktrees);
3411
+ this.restoreSelection(previousBranch);
3412
+ this.refreshCountdown = PANEL_REFRESH_INTERVAL_MS / 1e3;
3413
+ this.render();
3414
+ }
3415
+ /**
3416
+ * 按分支名恢复选中位置(基于显示顺序)
3417
+ * @param {string | undefined} previousBranch - 之前选中的分支名
3418
+ */
3419
+ restoreSelection(previousBranch) {
3420
+ if (!this.statusResult || !previousBranch || this.displayOrder.length === 0) {
3421
+ this.selectedDisplayIndex = 0;
3422
+ return;
3423
+ }
3424
+ const newDisplayIndex = this.displayOrder.findIndex(
3425
+ (origIdx) => this.statusResult.worktrees[origIdx]?.branch === previousBranch
3426
+ );
3427
+ if (newDisplayIndex >= 0) {
3428
+ this.selectedDisplayIndex = newDisplayIndex;
3429
+ } else {
3430
+ this.selectedDisplayIndex = Math.min(this.selectedDisplayIndex, Math.max(0, this.displayOrder.length - 1));
3431
+ }
3432
+ this.adjustScrollForSelection();
3433
+ }
3434
+ /**
3435
+ * 渲染一帧面板内容
3436
+ * 使用同步输出防止闪烁
3437
+ */
3438
+ render() {
3439
+ if (this.stopped || this.isOperating || !this.statusResult) return;
3440
+ const cols = process.stdout.columns || DEFAULT_TERMINAL_COLUMNS;
3441
+ const rows = process.stdout.rows || 24;
3442
+ const frameLines = buildPanelFrame(
3443
+ this.statusResult,
3444
+ this.getSelectedOriginalIndex(),
3445
+ this.scrollOffset,
3446
+ rows,
3447
+ cols,
3448
+ this.refreshCountdown
3449
+ );
3450
+ process.stdout.write(SYNC_OUTPUT_START);
3451
+ process.stdout.write(CLEAR_SCREEN);
3452
+ process.stdout.write(CURSOR_HOME);
3453
+ for (let i = 0; i < frameLines.length; i++) {
3454
+ const suffix = i < frameLines.length - 1 ? "\n" : "";
3455
+ process.stdout.write(`${truncateToTerminalWidth(frameLines[i], cols)}${suffix}`);
3456
+ }
3457
+ process.stdout.write(SYNC_OUTPUT_END);
3458
+ }
3459
+ /**
3460
+ * 执行操作:暂停面板 → 恢复终端 → 执行命令 → 等待回车 → 恢复面板
3461
+ * @param {() => void} action - 要执行的操作
3462
+ */
3463
+ async executeOperation(action) {
3464
+ if (!this.statusResult || this.displayOrder.length === 0) return;
3465
+ this.isOperating = true;
3466
+ this.clearTimers();
3467
+ this.restoreTerminal();
3468
+ this.stopKeyboardListener();
3469
+ action();
3470
+ console.log(PANEL_PRESS_ENTER_TO_RETURN);
3471
+ await this.waitForEnter();
3472
+ this.initTerminal();
3473
+ this.startKeyboardListener();
3474
+ this.isOperating = false;
3475
+ this.refreshData();
3476
+ this.startAutoRefresh();
3477
+ this.render();
3478
+ }
3479
+ /**
3480
+ * 获取当前选中的分支名
3481
+ * @returns {string} 当前选中的分支名
3482
+ */
3483
+ getSelectedBranch() {
3484
+ const originalIndex = this.getSelectedOriginalIndex();
3485
+ return this.statusResult.worktrees[originalIndex].branch;
3486
+ }
3487
+ /**
3488
+ * 执行验证操作
3489
+ */
3490
+ handleValidate() {
3491
+ const branch = this.getSelectedBranch();
3492
+ runCommandInherited(`clawt validate -b ${branch}`);
3493
+ }
3494
+ /**
3495
+ * 执行合并操作
3496
+ */
3497
+ handleMerge() {
3498
+ const branch = this.getSelectedBranch();
3499
+ runCommandInherited(`clawt merge -b ${branch}`);
3500
+ }
3501
+ /**
3502
+ * 执行删除操作
3503
+ */
3504
+ handleDelete() {
3505
+ const branch = this.getSelectedBranch();
3506
+ runCommandInherited(`clawt remove -b ${branch}`);
3507
+ }
3508
+ /**
3509
+ * 执行恢复操作
3510
+ */
3511
+ handleResume() {
3512
+ const branch = this.getSelectedBranch();
3513
+ runCommandInherited(`clawt resume -b ${branch}`);
3514
+ }
3515
+ /**
3516
+ * 执行同步操作
3517
+ */
3518
+ handleSync() {
3519
+ const branch = this.getSelectedBranch();
3520
+ runCommandInherited(`clawt sync -b ${branch}`);
3521
+ }
3522
+ /**
3523
+ * 等待用户按回车键
3524
+ * @returns {Promise<void>} 用户按回车时 resolve
3525
+ */
3526
+ waitForEnter() {
3527
+ return new Promise((resolve3) => {
3528
+ const rl = createInterface2({
3529
+ input: process.stdin,
3530
+ output: process.stdout
3531
+ });
3532
+ rl.once("line", () => {
3533
+ rl.close();
3534
+ resolve3();
3535
+ });
3536
+ });
3537
+ }
3538
+ };
3539
+
2670
3540
  // src/commands/list.ts
2671
- import chalk8 from "chalk";
3541
+ import chalk10 from "chalk";
2672
3542
  function registerListCommand(program2) {
2673
- program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
3543
+ program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree\uFF08\u652F\u6301 --json \u683C\u5F0F\u8F93\u51FA\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
2674
3544
  handleList(options);
2675
3545
  });
2676
3546
  }
@@ -2705,12 +3575,12 @@ function printListAsText(projectName, worktrees) {
2705
3575
  for (const wt of worktrees) {
2706
3576
  const status = getWorktreeStatus(wt);
2707
3577
  const isIdle = status ? isWorktreeIdle(status) : false;
2708
- const pathDisplay = isIdle ? chalk8.hex("#FF8C00")(wt.path) : wt.path;
3578
+ const pathDisplay = isIdle ? chalk10.hex("#FF8C00")(wt.path) : wt.path;
2709
3579
  printInfo(` ${pathDisplay} [${wt.branch}]`);
2710
3580
  if (status) {
2711
3581
  printInfo(` ${formatWorktreeStatus(status)}`);
2712
3582
  } else {
2713
- printInfo(` ${chalk8.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
3583
+ printInfo(` ${chalk10.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
2714
3584
  }
2715
3585
  printInfo("");
2716
3586
  }
@@ -2720,12 +3590,13 @@ function printListAsText(projectName, worktrees) {
2720
3590
 
2721
3591
  // src/commands/create.ts
2722
3592
  function registerCreateCommand(program2) {
2723
- program2.command("create").description("\u6279\u91CF\u521B\u5EFA worktree \u53CA\u5BF9\u5E94\u5206\u652F").requiredOption("-b, --branch <branchName>", "\u5206\u652F\u540D").option("-n, --number <count>", "\u521B\u5EFA\u6570\u91CF", "1").action((options) => {
2724
- handleCreate(options);
3593
+ program2.command("create").description("\u6279\u91CF\u521B\u5EFA worktree \u53CA\u5BF9\u5E94\u5206\u652F\uFF08\u542B\u9A8C\u8BC1\u5206\u652F\uFF09").requiredOption("-b, --branch <branchName>", "\u5206\u652F\u540D").option("-n, --number <count>", "\u521B\u5EFA\u6570\u91CF", "1").action(async (options) => {
3594
+ await handleCreate(options);
2725
3595
  });
2726
3596
  }
2727
- function handleCreate(options) {
3597
+ async function handleCreate(options) {
2728
3598
  validateMainWorktree();
3599
+ await ensureOnMainWorkBranch();
2729
3600
  const count = Number(options.number);
2730
3601
  if (!Number.isInteger(count) || count <= 0) {
2731
3602
  throw new ClawtError(
@@ -2741,6 +3612,7 @@ function handleCreate(options) {
2741
3612
  printInfo(`\u76EE\u5F55\u8DEF\u5F84${index + 1}\uFF1A`);
2742
3613
  printInfo(` ${wt.path}`);
2743
3614
  printInfo(` \u5206\u652F\u540D: ${wt.branch}`);
3615
+ printInfo(` \u9A8C\u8BC1\u5206\u652F: ${getValidateBranchName(wt.branch)}`);
2744
3616
  printSeparator();
2745
3617
  });
2746
3618
  }
@@ -2753,7 +3625,7 @@ var REMOVE_RESOLVE_MESSAGES = {
2753
3625
  noMatch: MESSAGES.REMOVE_NO_MATCH
2754
3626
  };
2755
3627
  function registerRemoveCommand(program2) {
2756
- program2.command("remove").description("\u79FB\u9664 worktree\uFF08\u652F\u6301\u5355\u4E2A/\u6279\u91CF/\u5168\u90E8\uFF09").option("--all", "\u79FB\u9664\u5F53\u524D\u9879\u76EE\u4E0B\u6240\u6709 worktree").option("-b, --branch <branchName>", "\u6307\u5B9A\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").action(async (options) => {
3628
+ program2.command("remove").description("\u79FB\u9664 worktree\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D/\u591A\u9009/\u5168\u90E8\uFF09").option("--all", "\u79FB\u9664\u5F53\u524D\u9879\u76EE\u4E0B\u6240\u6709 worktree").option("-b, --branch <branchName>", "\u6307\u5B9A\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").action(async (options) => {
2757
3629
  await handleRemove(options);
2758
3630
  });
2759
3631
  }
@@ -2774,13 +3646,13 @@ async function handleRemove(options) {
2774
3646
  }
2775
3647
  printInfo("\u5373\u5C06\u79FB\u9664\u4EE5\u4E0B worktree \u53CA\u672C\u5730\u5206\u652F\uFF1A\n");
2776
3648
  worktreesToRemove.forEach((wt, index) => {
2777
- printInfo(` ${index + 1}. ${wt.path} \u2192 \u5206\u652F: ${wt.branch}`);
3649
+ printInfo(` ${index + 1}. ${wt.path} \u2192 \u5206\u652F: ${wt.branch} \u9A8C\u8BC1\u5206\u652F: ${getValidateBranchName(wt.branch)}`);
2778
3650
  });
2779
3651
  printInfo("");
2780
3652
  const autoDelete = getConfigValue("autoDeleteBranch");
2781
3653
  let shouldDeleteBranch = autoDelete;
2782
3654
  if (!autoDelete) {
2783
- shouldDeleteBranch = await confirmAction("\u662F\u5426\u540C\u65F6\u5220\u9664\u5BF9\u5E94\u7684\u672C\u5730\u5206\u652F\uFF1F");
3655
+ shouldDeleteBranch = await confirmAction(MESSAGES.REMOVE_CONFIRM_DELETE_BRANCHES);
2784
3656
  if (!shouldDeleteBranch) {
2785
3657
  printHint(MESSAGES.REMOVE_BRANCHES_KEPT);
2786
3658
  }
@@ -2788,10 +3660,12 @@ async function handleRemove(options) {
2788
3660
  const failures = [];
2789
3661
  for (const wt of worktreesToRemove) {
2790
3662
  try {
3663
+ await ensureOnMainWorkBranch();
2791
3664
  removeWorktreeByPath(wt.path);
2792
3665
  if (shouldDeleteBranch) {
2793
3666
  deleteBranch(wt.branch);
2794
3667
  }
3668
+ deleteValidateBranch(wt.branch);
2795
3669
  removeSnapshot(projectName, wt.branch);
2796
3670
  printSuccess(MESSAGES.WORKTREE_REMOVED(wt.path));
2797
3671
  } catch (error) {
@@ -2816,7 +3690,7 @@ async function handleRemove(options) {
2816
3690
 
2817
3691
  // src/commands/run.ts
2818
3692
  function registerRunCommand(program2) {
2819
- program2.command("run").description("\u6279\u91CF\u521B\u5EFA worktree \u5E76\u542F\u52A8 Claude Code \u6267\u884C\u4EFB\u52A1").option("-b, --branch <branchName>", "\u5206\u652F\u540D").option("--tasks <task...>", "\u4EFB\u52A1\u5217\u8868\uFF08\u53EF\u591A\u6B21\u6307\u5B9A\uFF09\uFF0C\u4E0D\u4F20\u5219\u5728 worktree \u4E2D\u6253\u5F00 Claude Code \u4EA4\u4E92\u5F0F\u754C\u9762").option("-c, --concurrency <n>", "\u6700\u5927\u5E76\u53D1\u6570\uFF0C0 \u8868\u793A\u4E0D\u9650\u5236").option("-f, --file <path>", "\u4ECE\u4EFB\u52A1\u6587\u4EF6\u8BFB\u53D6\u4EFB\u52A1\u5217\u8868\uFF08\u4E0E --tasks \u4E92\u65A5\uFF09").option("-d, --dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4EC5\u5C55\u793A\u4EFB\u52A1\u8BA1\u5212\u4E0D\u5B9E\u9645\u6267\u884C").action(async (options) => {
3693
+ program2.command("run").description("\u6279\u91CF\u521B\u5EFA worktree + \u542F\u52A8 Claude Code \u6267\u884C\u4EFB\u52A1\uFF08\u652F\u6301\u4EFB\u52A1\u6587\u4EF6\uFF09").option("-b, --branch <branchName>", "\u5206\u652F\u540D").option("--tasks <task...>", "\u4EFB\u52A1\u5217\u8868\uFF08\u53EF\u591A\u6B21\u6307\u5B9A\uFF09\uFF0C\u4E0D\u4F20\u5219\u5728 worktree \u4E2D\u6253\u5F00 Claude Code \u4EA4\u4E92\u5F0F\u754C\u9762").option("-c, --concurrency <n>", "\u6700\u5927\u5E76\u53D1\u6570\uFF0C0 \u8868\u793A\u4E0D\u9650\u5236").option("-f, --file <path>", "\u4ECE\u4EFB\u52A1\u6587\u4EF6\u8BFB\u53D6\u4EFB\u52A1\u5217\u8868\uFF08\u4E0E --tasks \u4E92\u65A5\uFF09").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4EC5\u5C55\u793A\u4EFB\u52A1\u8BA1\u5212\u4E0D\u5B9E\u9645\u6267\u884C").action(async (options) => {
2820
3694
  await handleRun(options);
2821
3695
  });
2822
3696
  }
@@ -2854,6 +3728,9 @@ function handleDryRunFromFile(options) {
2854
3728
  }
2855
3729
  async function handleRun(options) {
2856
3730
  validateMainWorktree();
3731
+ if (!options.dryRun) {
3732
+ await ensureOnMainWorkBranch();
3733
+ }
2857
3734
  if (options.file && options.tasks) {
2858
3735
  throw new ClawtError(MESSAGES.FILE_AND_TASKS_CONFLICT);
2859
3736
  }
@@ -2909,7 +3786,7 @@ var RESUME_RESOLVE_MESSAGES = {
2909
3786
  noMatch: MESSAGES.RESUME_NO_MATCH
2910
3787
  };
2911
3788
  function registerResumeCommand(program2) {
2912
- program2.command("resume").description("\u5728\u5DF2\u6709 worktree \u4E2D\u6062\u590D Claude Code \u4EA4\u4E92\u5F0F\u4F1A\u8BDD").option("-b, --branch <branchName>", "\u8981\u6062\u590D\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").action(async (options) => {
3789
+ program2.command("resume").description("\u5728\u5DF2\u6709 worktree \u4E2D\u6062\u590D Claude Code \u4F1A\u8BDD\uFF08\u652F\u6301\u591A\u9009\u6279\u91CF\u6062\u590D\uFF09").option("-b, --branch <branchName>", "\u8981\u6062\u590D\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").action(async (options) => {
2913
3790
  await handleResume(options);
2914
3791
  });
2915
3792
  }
@@ -2961,9 +3838,6 @@ async function handleBatchResume(worktrees) {
2961
3838
  printSuccess(MESSAGES.RESUME_ALL_SUCCESS(worktrees.length));
2962
3839
  }
2963
3840
 
2964
- // src/commands/validate.ts
2965
- import Enquirer4 from "enquirer";
2966
-
2967
3841
  // src/commands/sync.ts
2968
3842
  function registerSyncCommand(program2) {
2969
3843
  program2.command("sync").description("\u5C06\u4E3B\u5206\u652F\u6700\u65B0\u4EE3\u7801\u540C\u6B65\u5230\u76EE\u6807 worktree").option("-b, --branch <branchName>", "\u8981\u540C\u6B65\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").action(async (options) => {
@@ -2993,9 +3867,9 @@ function mergeMainBranch(worktreePath, mainBranch) {
2993
3867
  throw new ClawtError(`\u5408\u5E76 ${mainBranch} \u5931\u8D25`);
2994
3868
  }
2995
3869
  }
2996
- function executeSyncForBranch(targetWorktreePath, branch) {
3870
+ async function executeSyncForBranch(targetWorktreePath, branch) {
2997
3871
  const mainWorktreePath = getGitTopLevel();
2998
- const mainBranch = getCurrentBranch(mainWorktreePath);
3872
+ const mainBranch = getMainWorkBranch();
2999
3873
  if (!isWorkingDirClean(targetWorktreePath)) {
3000
3874
  autoSaveChanges(targetWorktreePath, branch);
3001
3875
  }
@@ -3011,15 +3885,20 @@ function executeSyncForBranch(targetWorktreePath, branch) {
3011
3885
  logger.info(`\u5DF2\u6E05\u9664\u5206\u652F ${branch} \u7684 validate \u5FEB\u7167`);
3012
3886
  }
3013
3887
  printSuccess(MESSAGES.SYNC_SUCCESS(branch, mainBranch));
3888
+ await rebuildValidateBranch(branch, mainWorktreePath);
3889
+ const validateBranchName = getValidateBranchName(branch);
3890
+ printInfo(MESSAGES.SYNC_VALIDATE_BRANCH_REBUILT(validateBranchName));
3014
3891
  return { success: true, hasConflict: false };
3015
3892
  }
3016
3893
  async function handleSync(options) {
3017
3894
  validateMainWorktree();
3895
+ requireProjectConfig();
3896
+ await ensureOnMainWorkBranch();
3018
3897
  logger.info(`sync \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch ?? "(\u672A\u6307\u5B9A)"}`);
3019
3898
  const worktrees = getProjectWorktrees();
3020
3899
  const worktree = await resolveTargetWorktree(worktrees, SYNC_RESOLVE_MESSAGES, options.branch);
3021
3900
  const { path: targetWorktreePath, branch } = worktree;
3022
- executeSyncForBranch(targetWorktreePath, branch);
3901
+ await executeSyncForBranch(targetWorktreePath, branch);
3023
3902
  }
3024
3903
 
3025
3904
  // src/commands/validate.ts
@@ -3030,43 +3909,12 @@ var VALIDATE_RESOLVE_MESSAGES = {
3030
3909
  noMatch: MESSAGES.VALIDATE_NO_MATCH
3031
3910
  };
3032
3911
  function registerValidateCommand(program2) {
3033
- program2.command("validate").description("\u5728\u4E3B worktree \u9A8C\u8BC1\u67D0\u4E2A worktree \u5206\u652F\u7684\u53D8\u66F4").option("-b, --branch <branchName>", "\u8981\u9A8C\u8BC1\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").option("--clean", "\u6E05\u7406 validate \u72B6\u6001\uFF08\u91CD\u7F6E\u4E3B worktree \u5E76\u5220\u9664\u5FEB\u7167\uFF09").option("-r, --run <command>", "validate \u6210\u529F\u540E\u5728\u4E3B worktree \u4E2D\u6267\u884C\u7684\u547D\u4EE4").action(async (options) => {
3912
+ program2.command("validate").description("\u5728\u4E3B worktree \u9A8C\u8BC1\u67D0\u4E2A worktree \u5206\u652F\u7684\u53D8\u66F4\uFF08\u901A\u8FC7\u9A8C\u8BC1\u5206\u652F\uFF09").option("-b, --branch <branchName>", "\u8981\u9A8C\u8BC1\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").option("--clean", "\u6E05\u7406 validate \u72B6\u6001\uFF08\u91CD\u7F6E\u4E3B worktree \u5E76\u5220\u9664\u5FEB\u7167\uFF09").option("-r, --run <command>", "validate \u6210\u529F\u540E\u5728\u4E3B worktree \u4E2D\u6267\u884C\u7684\u547D\u4EE4").action(async (options) => {
3034
3913
  await handleValidate(options);
3035
3914
  });
3036
3915
  }
3037
3916
  async function handleDirtyMainWorktree(mainWorktreePath) {
3038
- printWarning("\u4E3B worktree \u5F53\u524D\u5206\u652F\u6709\u672A\u63D0\u4EA4\u7684\u66F4\u6539\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F\uFF1A\n");
3039
- const choice = await new Enquirer4.Select({
3040
- message: "\u9009\u62E9\u5904\u7406\u65B9\u5F0F",
3041
- choices: [
3042
- {
3043
- name: "reset",
3044
- message: "reset (\u63A8\u8350) - \u4E22\u5F03\u6240\u6709\u66F4\u6539 (git reset --hard HEAD && git clean -fd)"
3045
- },
3046
- {
3047
- name: "stash",
3048
- message: "stash - \u6682\u5B58\u66F4\u6539 (git add . && git stash)"
3049
- },
3050
- {
3051
- name: "exit",
3052
- message: "exit - \u9000\u51FA\uFF0C\u624B\u52A8\u5904\u7406"
3053
- }
3054
- ],
3055
- initial: 0
3056
- }).run();
3057
- if (choice === "exit") {
3058
- throw new ClawtError("\u7528\u6237\u9009\u62E9\u9000\u51FA");
3059
- }
3060
- if (choice === "reset") {
3061
- gitResetHard(mainWorktreePath);
3062
- gitCleanForce(mainWorktreePath);
3063
- } else if (choice === "stash") {
3064
- gitAddAll(mainWorktreePath);
3065
- gitStashPush("clawt:auto-stash", mainWorktreePath);
3066
- }
3067
- if (!isWorkingDirClean(mainWorktreePath)) {
3068
- throw new ClawtError("\u5DE5\u4F5C\u533A\u4ECD\u7136\u4E0D\u5E72\u51C0\uFF0C\u8BF7\u624B\u52A8\u5904\u7406");
3069
- }
3917
+ await handleDirtyWorkingDir(mainWorktreePath);
3070
3918
  }
3071
3919
  function migrateChangesViaPatch(targetWorktreePath, mainWorktreePath, branchName, hasUncommitted) {
3072
3920
  let didTempCommit = false;
@@ -3109,7 +3957,7 @@ async function handlePatchApplyFailure(targetWorktreePath, branchName) {
3109
3957
  return;
3110
3958
  }
3111
3959
  printInfo(MESSAGES.VALIDATE_AUTO_SYNC_START(branchName));
3112
- const syncResult = executeSyncForBranch(targetWorktreePath, branchName);
3960
+ const syncResult = await executeSyncForBranch(targetWorktreePath, branchName);
3113
3961
  }
3114
3962
  function saveCurrentSnapshotTree(mainWorktreePath, projectName, branchName) {
3115
3963
  gitAddAll(mainWorktreePath);
@@ -3121,6 +3969,7 @@ function saveCurrentSnapshotTree(mainWorktreePath, projectName, branchName) {
3121
3969
  }
3122
3970
  async function handleValidateClean(options) {
3123
3971
  validateMainWorktree();
3972
+ requireProjectConfig();
3124
3973
  const projectName = getProjectName();
3125
3974
  const mainWorktreePath = getGitTopLevel();
3126
3975
  const worktrees = getProjectWorktrees();
@@ -3141,17 +3990,24 @@ async function handleValidateClean(options) {
3141
3990
  gitResetHard(mainWorktreePath);
3142
3991
  gitCleanForce(mainWorktreePath);
3143
3992
  }
3993
+ await ensureOnMainWorkBranch(mainWorktreePath);
3144
3994
  removeSnapshot(projectName, branchName);
3145
3995
  printSuccess(MESSAGES.VALIDATE_CLEANED(branchName));
3146
3996
  }
3147
3997
  async function handleFirstValidate(targetWorktreePath, mainWorktreePath, projectName, branchName, hasUncommitted) {
3998
+ const validateBranchName = getValidateBranchName(branchName);
3999
+ if (!checkBranchExists(validateBranchName)) {
4000
+ throw new ClawtError(MESSAGES.VALIDATE_BRANCH_NOT_FOUND(validateBranchName, branchName));
4001
+ }
4002
+ gitCheckout(validateBranchName, mainWorktreePath);
3148
4003
  const result = migrateChangesViaPatch(targetWorktreePath, mainWorktreePath, branchName, hasUncommitted);
3149
4004
  if (!result.success) {
4005
+ await ensureOnMainWorkBranch(mainWorktreePath);
3150
4006
  await handlePatchApplyFailure(targetWorktreePath, branchName);
3151
4007
  return;
3152
4008
  }
3153
4009
  saveCurrentSnapshotTree(mainWorktreePath, projectName, branchName);
3154
- printSuccess(MESSAGES.VALIDATE_SUCCESS(branchName));
4010
+ printSuccess(MESSAGES.VALIDATE_SUCCESS_WITH_BRANCH(branchName, validateBranchName));
3155
4011
  }
3156
4012
  async function handleIncrementalValidate(targetWorktreePath, mainWorktreePath, projectName, branchName, hasUncommitted) {
3157
4013
  const { treeHash: oldTreeHash, headCommitHash: oldHeadCommitHash } = readSnapshot(projectName, branchName);
@@ -3159,8 +4015,17 @@ async function handleIncrementalValidate(targetWorktreePath, mainWorktreePath, p
3159
4015
  gitResetHard(mainWorktreePath);
3160
4016
  gitCleanForce(mainWorktreePath);
3161
4017
  }
4018
+ const validateBranchName = getValidateBranchName(branchName);
4019
+ if (!checkBranchExists(validateBranchName)) {
4020
+ throw new ClawtError(MESSAGES.VALIDATE_BRANCH_NOT_FOUND(validateBranchName, branchName));
4021
+ }
4022
+ const currentBranch = getCurrentBranch(mainWorktreePath);
4023
+ if (currentBranch !== validateBranchName) {
4024
+ gitCheckout(validateBranchName, mainWorktreePath);
4025
+ }
3162
4026
  const result = migrateChangesViaPatch(targetWorktreePath, mainWorktreePath, branchName, hasUncommitted);
3163
4027
  if (!result.success) {
4028
+ await ensureOnMainWorkBranch(mainWorktreePath);
3164
4029
  await handlePatchApplyFailure(targetWorktreePath, branchName);
3165
4030
  return;
3166
4031
  }
@@ -3175,7 +4040,7 @@ async function handleIncrementalValidate(targetWorktreePath, mainWorktreePath, p
3175
4040
  } else if (oldChangePatch.length > 0) {
3176
4041
  logger.warn("\u65E7\u53D8\u66F4 patch \u4E0E\u5F53\u524D HEAD \u51B2\u7A81\uFF0C\u964D\u7EA7\u4E3A\u5168\u91CF\u6A21\u5F0F");
3177
4042
  printWarning(MESSAGES.INCREMENTAL_VALIDATE_FALLBACK);
3178
- printSuccess(MESSAGES.VALIDATE_SUCCESS(branchName));
4043
+ printSuccess(MESSAGES.VALIDATE_SUCCESS_WITH_BRANCH(branchName, validateBranchName));
3179
4044
  return;
3180
4045
  }
3181
4046
  } else {
@@ -3184,7 +4049,7 @@ async function handleIncrementalValidate(targetWorktreePath, mainWorktreePath, p
3184
4049
  } catch (error) {
3185
4050
  logger.warn(`\u589E\u91CF read-tree \u5931\u8D25: ${error}`);
3186
4051
  printWarning(MESSAGES.INCREMENTAL_VALIDATE_FALLBACK);
3187
- printSuccess(MESSAGES.VALIDATE_SUCCESS(branchName));
4052
+ printSuccess(MESSAGES.VALIDATE_SUCCESS_WITH_BRANCH(branchName, validateBranchName));
3188
4053
  return;
3189
4054
  }
3190
4055
  printSuccess(MESSAGES.INCREMENTAL_VALIDATE_SUCCESS(branchName));
@@ -3248,6 +4113,7 @@ async function handleValidate(options) {
3248
4113
  return;
3249
4114
  }
3250
4115
  validateMainWorktree();
4116
+ requireProjectConfig();
3251
4117
  const projectName = getProjectName();
3252
4118
  const mainWorktreePath = getGitTopLevel();
3253
4119
  const worktrees = getProjectWorktrees();
@@ -3280,7 +4146,7 @@ async function handleValidate(options) {
3280
4146
 
3281
4147
  // src/commands/merge.ts
3282
4148
  function registerMergeCommand(program2) {
3283
- program2.command("merge").description("\u5408\u5E76\u67D0\u4E2A\u5DF2\u9A8C\u8BC1\u7684 worktree \u5206\u652F\u5230\u4E3B worktree").option("-b, --branch <branchName>", "\u8981\u5408\u5E76\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").option("-m, --message <message>", "\u63D0\u4EA4\u4FE1\u606F\uFF08\u5DE5\u4F5C\u533A\u6709\u4FEE\u6539\u65F6\u5FC5\u586B\uFF09").action(async (options) => {
4149
+ program2.command("merge").description("\u5408\u5E76\u67D0\u4E2A\u5DF2\u9A8C\u8BC1\u7684 worktree \u5206\u652F\u5230\u4E3B worktree").option("-b, --branch <branchName>", "\u8981\u5408\u5E76\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\u4F9B\u9009\u62E9\uFF09").option("-m, --message <commitMessage>", "\u63D0\u4EA4\u4FE1\u606F\uFF08\u76EE\u6807 worktree \u5DE5\u4F5C\u533A\u6709\u4FEE\u6539\u65F6\u5FC5\u586B\uFF09").action(async (options) => {
3284
4150
  await handleMerge(options);
3285
4151
  });
3286
4152
  }
@@ -3298,7 +4164,7 @@ async function handleSquashIfNeeded(targetWorktreePath, mainWorktreePath, branch
3298
4164
  if (!shouldSquash) {
3299
4165
  return false;
3300
4166
  }
3301
- const mainBranch = getCurrentBranch(mainWorktreePath);
4167
+ const mainBranch = getMainWorkBranch();
3302
4168
  const mergeBase = gitMergeBase(mainBranch, branchName, mainWorktreePath);
3303
4169
  logger.info(`squash: merge-base = ${mergeBase}, \u5206\u652F = ${branchName}`);
3304
4170
  gitResetSoftTo(mergeBase, targetWorktreePath);
@@ -3325,6 +4191,7 @@ function cleanupWorktreeAndBranch(worktreePath, branchName) {
3325
4191
  async function handleMerge(options) {
3326
4192
  validateMainWorktree();
3327
4193
  const mainWorktreePath = getGitTopLevel();
4194
+ await ensureOnMainWorkBranch(mainWorktreePath);
3328
4195
  logger.info(`merge \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch ?? "(\u672A\u6307\u5B9A)"}\uFF0C\u63D0\u4EA4\u4FE1\u606F: ${options.message ?? "(\u672A\u63D0\u4F9B)"}`);
3329
4196
  const worktrees = getProjectWorktrees();
3330
4197
  const worktree = await resolveTargetWorktree(worktrees, MERGE_RESOLVE_MESSAGES, options.branch);
@@ -3398,7 +4265,7 @@ async function handleMerge(options) {
3398
4265
  }
3399
4266
 
3400
4267
  // src/commands/config.ts
3401
- import chalk9 from "chalk";
4268
+ import chalk11 from "chalk";
3402
4269
  import Enquirer5 from "enquirer";
3403
4270
  function registerConfigCommand(program2) {
3404
4271
  const configCmd = program2.command("config").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u5168\u5C40\u914D\u7F6E").action(async () => {
@@ -3459,7 +4326,7 @@ async function handleInteractiveConfigSet() {
3459
4326
  const isObject = typeof DEFAULT_CONFIG[k] === "object";
3460
4327
  return {
3461
4328
  name: k,
3462
- message: `${k}: ${isObject ? chalk9.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk9.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
4329
+ message: `${k}: ${isObject ? chalk11.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk11.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
3463
4330
  ...isObject && { disabled: CONFIG_ALIAS_DISABLED_HINT }
3464
4331
  };
3465
4332
  });
@@ -3492,6 +4359,7 @@ function registerResetCommand(program2) {
3492
4359
  }
3493
4360
  async function handleReset() {
3494
4361
  validateMainWorktree();
4362
+ requireProjectConfig();
3495
4363
  const mainWorktreePath = getGitTopLevel();
3496
4364
  logger.info("reset \u547D\u4EE4\u6267\u884C");
3497
4365
  if (!isWorkingDirClean(mainWorktreePath)) {
@@ -3514,14 +4382,19 @@ async function handleReset() {
3514
4382
  }
3515
4383
 
3516
4384
  // src/commands/status.ts
3517
- import chalk10 from "chalk";
4385
+ import chalk12 from "chalk";
3518
4386
  function registerStatusCommand(program2) {
3519
- program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
3520
- handleStatus(options);
4387
+ program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8\uFF08\u652F\u6301 --json \u683C\u5F0F\u8F93\u51FA\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9762\u677F\u6A21\u5F0F").action(async (options) => {
4388
+ await handleStatus(options);
3521
4389
  });
3522
4390
  }
3523
- function handleStatus(options) {
4391
+ async function handleStatus(options) {
3524
4392
  validateMainWorktree();
4393
+ if (options.interactive) {
4394
+ const panel = new InteractivePanel(collectStatus);
4395
+ await panel.start();
4396
+ return;
4397
+ }
3525
4398
  const statusResult = collectStatus();
3526
4399
  logger.info(`status \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${statusResult.main.projectName}\uFF0C\u5171 ${statusResult.totalWorktrees} \u4E2A worktree`);
3527
4400
  if (options.json) {
@@ -3627,7 +4500,7 @@ function printStatusAsJson(result) {
3627
4500
  }
3628
4501
  function printStatusAsText(result) {
3629
4502
  printDoubleSeparator();
3630
- printInfo(` ${chalk10.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
4503
+ printInfo(` ${chalk12.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
3631
4504
  printDoubleSeparator();
3632
4505
  printInfo("");
3633
4506
  printMainSection(result.main);
@@ -3640,17 +4513,17 @@ function printStatusAsText(result) {
3640
4513
  printDoubleSeparator();
3641
4514
  }
3642
4515
  function printMainSection(main2) {
3643
- printInfo(` ${chalk10.bold("\u25C6")} ${chalk10.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
3644
- printInfo(` \u5206\u652F: ${chalk10.bold(main2.branch)}`);
4516
+ printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
4517
+ printInfo(` \u5206\u652F: ${chalk12.bold(main2.branch)}`);
3645
4518
  if (main2.isClean) {
3646
- printInfo(` \u72B6\u6001: ${chalk10.green("\u2713 \u5E72\u51C0")}`);
4519
+ printInfo(` \u72B6\u6001: ${chalk12.green("\u2713 \u5E72\u51C0")}`);
3647
4520
  } else {
3648
- printInfo(` \u72B6\u6001: ${chalk10.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
4521
+ printInfo(` \u72B6\u6001: ${chalk12.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
3649
4522
  }
3650
4523
  printInfo("");
3651
4524
  }
3652
4525
  function printWorktreesSection(worktrees, total) {
3653
- printInfo(` ${chalk10.bold("\u25C6")} ${chalk10.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
4526
+ printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
3654
4527
  printInfo("");
3655
4528
  if (worktrees.length === 0) {
3656
4529
  printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
@@ -3661,57 +4534,57 @@ function printWorktreesSection(worktrees, total) {
3661
4534
  }
3662
4535
  }
3663
4536
  function printWorktreeItem(wt) {
3664
- const statusLabel = formatChangeStatusLabel(wt.changeStatus);
3665
- printInfo(` ${chalk10.bold("\u25CF")} ${chalk10.bold(wt.branch)} [${statusLabel}]`);
4537
+ const statusLabel = formatChangeStatusLabel2(wt.changeStatus);
4538
+ printInfo(` ${chalk12.bold("\u25CF")} ${chalk12.bold(wt.branch)} [${statusLabel}]`);
3666
4539
  if (wt.insertions > 0 || wt.deletions > 0) {
3667
- printInfo(` ${chalk10.green(`+${wt.insertions}`)} ${chalk10.red(`-${wt.deletions}`)}`);
4540
+ printInfo(` ${chalk12.green(`+${wt.insertions}`)} ${chalk12.red(`-${wt.deletions}`)}`);
3668
4541
  }
3669
4542
  if (wt.commitsAhead > 0) {
3670
- printInfo(` ${chalk10.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
4543
+ printInfo(` ${chalk12.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
3671
4544
  }
3672
4545
  if (wt.commitsBehind > 0) {
3673
- printInfo(` ${chalk10.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
4546
+ printInfo(` ${chalk12.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
3674
4547
  } else {
3675
- printInfo(` ${chalk10.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
4548
+ printInfo(` ${chalk12.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
3676
4549
  }
3677
4550
  if (wt.createdAt) {
3678
4551
  const relativeTime = formatRelativeTime(wt.createdAt);
3679
4552
  if (relativeTime) {
3680
- printInfo(` ${chalk10.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
4553
+ printInfo(` ${chalk12.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
3681
4554
  }
3682
4555
  }
3683
4556
  if (wt.snapshotTime) {
3684
4557
  const relativeTime = formatRelativeTime(wt.snapshotTime);
3685
4558
  if (relativeTime) {
3686
- printInfo(` ${chalk10.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
4559
+ printInfo(` ${chalk12.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
3687
4560
  }
3688
4561
  } else {
3689
- printInfo(` ${chalk10.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
4562
+ printInfo(` ${chalk12.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
3690
4563
  }
3691
4564
  printInfo("");
3692
4565
  }
3693
- function formatChangeStatusLabel(status) {
4566
+ function formatChangeStatusLabel2(status) {
3694
4567
  switch (status) {
3695
4568
  case "committed":
3696
- return chalk10.green(MESSAGES.STATUS_CHANGE_COMMITTED);
4569
+ return chalk12.green(MESSAGES.STATUS_CHANGE_COMMITTED);
3697
4570
  case "uncommitted":
3698
- return chalk10.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
4571
+ return chalk12.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
3699
4572
  case "conflict":
3700
- return chalk10.red(MESSAGES.STATUS_CHANGE_CONFLICT);
4573
+ return chalk12.red(MESSAGES.STATUS_CHANGE_CONFLICT);
3701
4574
  case "clean":
3702
- return chalk10.gray(MESSAGES.STATUS_CHANGE_CLEAN);
4575
+ return chalk12.gray(MESSAGES.STATUS_CHANGE_CLEAN);
3703
4576
  }
3704
4577
  }
3705
4578
  function printSnapshotsSection(snapshots) {
3706
- printInfo(` ${chalk10.bold("\u25C6")} ${chalk10.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
4579
+ printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
3707
4580
  if (snapshots.orphaned > 0) {
3708
- printInfo(` ${chalk10.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
4581
+ printInfo(` ${chalk12.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
3709
4582
  }
3710
4583
  printInfo("");
3711
4584
  }
3712
4585
 
3713
4586
  // src/commands/alias.ts
3714
- import chalk11 from "chalk";
4587
+ import chalk13 from "chalk";
3715
4588
  function getRegisteredCommandNames(program2) {
3716
4589
  return program2.commands.map((cmd) => cmd.name());
3717
4590
  }
@@ -3732,7 +4605,7 @@ ${MESSAGES.ALIAS_LIST_TITLE}
3732
4605
  `);
3733
4606
  printSeparator();
3734
4607
  for (const [alias, command] of entries) {
3735
- printInfo(` ${chalk11.bold(alias)} \u2192 ${chalk11.cyan(command)}`);
4608
+ printInfo(` ${chalk13.bold(alias)} \u2192 ${chalk13.cyan(command)}`);
3736
4609
  }
3737
4610
  printInfo("");
3738
4611
  printSeparator();
@@ -3764,7 +4637,7 @@ function handleAliasRemove(alias) {
3764
4637
  printSuccess(MESSAGES.ALIAS_REMOVE_SUCCESS(alias));
3765
4638
  }
3766
4639
  function registerAliasCommand(program2) {
3767
- const aliasCmd = program2.command("alias").description("\u7BA1\u7406\u547D\u4EE4\u522B\u540D").action(() => {
4640
+ const aliasCmd = program2.command("alias").description("\u7BA1\u7406\u547D\u4EE4\u522B\u540D\uFF08\u5217\u51FA / \u8BBE\u7F6E / \u79FB\u9664\uFF09").action(() => {
3768
4641
  handleAliasList();
3769
4642
  });
3770
4643
  aliasCmd.command("list").description("\u5217\u51FA\u6240\u6709\u522B\u540D").action(() => {
@@ -3779,9 +4652,9 @@ function registerAliasCommand(program2) {
3779
4652
  }
3780
4653
 
3781
4654
  // src/commands/projects.ts
3782
- import { existsSync as existsSync9, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
3783
- import { join as join7 } from "path";
3784
- import chalk12 from "chalk";
4655
+ import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
4656
+ import { join as join8 } from "path";
4657
+ import chalk14 from "chalk";
3785
4658
  function registerProjectsCommand(program2) {
3786
4659
  program2.command("projects [name]").description("\u5C55\u793A\u6240\u6709\u9879\u76EE\u7684 worktree \u6982\u89C8\uFF0C\u6216\u67E5\u770B\u6307\u5B9A\u9879\u76EE\u7684 worktree \u8BE6\u60C5").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((name, options) => {
3787
4660
  handleProjects({ name, json: options.json });
@@ -3804,8 +4677,8 @@ function handleProjectsOverview(json) {
3804
4677
  printProjectsOverviewAsText(result);
3805
4678
  }
3806
4679
  function handleProjectDetail(name, json) {
3807
- const projectDir = join7(WORKTREES_DIR, name);
3808
- if (!existsSync9(projectDir)) {
4680
+ const projectDir = join8(WORKTREES_DIR, name);
4681
+ if (!existsSync10(projectDir)) {
3809
4682
  printError(MESSAGES.PROJECTS_NOT_FOUND(name));
3810
4683
  process.exit(1);
3811
4684
  }
@@ -3818,7 +4691,7 @@ function handleProjectDetail(name, json) {
3818
4691
  printProjectDetailAsText(result);
3819
4692
  }
3820
4693
  function collectProjectsOverview() {
3821
- if (!existsSync9(WORKTREES_DIR)) {
4694
+ if (!existsSync10(WORKTREES_DIR)) {
3822
4695
  return { projects: [], totalProjects: 0, totalDiskUsage: 0 };
3823
4696
  }
3824
4697
  const entries = readdirSync5(WORKTREES_DIR, { withFileTypes: true });
@@ -3827,7 +4700,7 @@ function collectProjectsOverview() {
3827
4700
  if (!entry.isDirectory()) {
3828
4701
  continue;
3829
4702
  }
3830
- const projectDir = join7(WORKTREES_DIR, entry.name);
4703
+ const projectDir = join8(WORKTREES_DIR, entry.name);
3831
4704
  const overview = collectSingleProjectOverview(entry.name, projectDir);
3832
4705
  projects.push(overview);
3833
4706
  }
@@ -3844,7 +4717,7 @@ function collectSingleProjectOverview(name, projectDir) {
3844
4717
  const worktreeDirs = subEntries.filter((e) => e.isDirectory());
3845
4718
  const worktreeCount = worktreeDirs.length;
3846
4719
  const diskUsage = calculateDirSize(projectDir);
3847
- const lastActiveTime = resolveProjectLastActiveTime(projectDir, worktreeDirs.map((e) => join7(projectDir, e.name)));
4720
+ const lastActiveTime = resolveProjectLastActiveTime(projectDir, worktreeDirs.map((e) => join8(projectDir, e.name)));
3848
4721
  return {
3849
4722
  name,
3850
4723
  worktreeCount,
@@ -3859,7 +4732,7 @@ function collectProjectDetail(name, projectDir) {
3859
4732
  if (!entry.isDirectory()) {
3860
4733
  continue;
3861
4734
  }
3862
- const wtPath = join7(projectDir, entry.name);
4735
+ const wtPath = join8(projectDir, entry.name);
3863
4736
  const detail = collectSingleWorktreeDetail(entry.name, wtPath);
3864
4737
  worktrees.push(detail);
3865
4738
  }
@@ -3900,7 +4773,7 @@ function sortByLastActiveTimeDesc(projects) {
3900
4773
  }
3901
4774
  function printProjectsOverviewAsText(result) {
3902
4775
  printDoubleSeparator();
3903
- printInfo(` ${chalk12.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
4776
+ printInfo(` ${chalk14.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
3904
4777
  printDoubleSeparator();
3905
4778
  printInfo("");
3906
4779
  if (result.projects.length === 0) {
@@ -3914,7 +4787,7 @@ function printProjectsOverviewAsText(result) {
3914
4787
  }
3915
4788
  printSeparator();
3916
4789
  printInfo("");
3917
- printInfo(` \u5171 ${chalk12.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk12.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
4790
+ printInfo(` \u5171 ${chalk14.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk14.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
3918
4791
  printInfo("");
3919
4792
  printDoubleSeparator();
3920
4793
  }
@@ -3922,16 +4795,16 @@ function printProjectOverviewItem(project) {
3922
4795
  const relativeTime = formatRelativeTime(project.lastActiveTime);
3923
4796
  const activeLabel = relativeTime ? MESSAGES.PROJECTS_LAST_ACTIVE(relativeTime) : "";
3924
4797
  const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(project.diskUsage));
3925
- printInfo(` ${chalk12.bold("\u25CF")} ${chalk12.bold(project.name)}`);
3926
- printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk12.gray(activeLabel)} ${chalk12.gray(diskLabel)}`);
4798
+ printInfo(` ${chalk14.bold("\u25CF")} ${chalk14.bold(project.name)}`);
4799
+ printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk14.gray(activeLabel)} ${chalk14.gray(diskLabel)}`);
3927
4800
  printInfo("");
3928
4801
  }
3929
4802
  function printProjectDetailAsText(result) {
3930
4803
  printDoubleSeparator();
3931
- printInfo(` ${chalk12.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
4804
+ printInfo(` ${chalk14.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
3932
4805
  printDoubleSeparator();
3933
4806
  printInfo("");
3934
- printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
4807
+ printInfo(` ${chalk14.bold("\u25C6")} ${chalk14.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
3935
4808
  printInfo(` ${MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage))}`);
3936
4809
  printInfo("");
3937
4810
  printSeparator();
@@ -3951,14 +4824,14 @@ function printWorktreeDetailItem(wt) {
3951
4824
  const relativeTime = formatRelativeTime(wt.lastModifiedTime);
3952
4825
  const modifiedLabel = relativeTime ? MESSAGES.PROJECTS_LAST_MODIFIED(relativeTime) : "";
3953
4826
  const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(wt.diskUsage));
3954
- printInfo(` ${chalk12.bold("\u25CF")} ${chalk12.bold(wt.branch)}`);
4827
+ printInfo(` ${chalk14.bold("\u25CF")} ${chalk14.bold(wt.branch)}`);
3955
4828
  printInfo(` ${wt.path}`);
3956
- printInfo(` ${chalk12.gray(modifiedLabel)} ${chalk12.gray(diskLabel)}`);
4829
+ printInfo(` ${chalk14.gray(modifiedLabel)} ${chalk14.gray(diskLabel)}`);
3957
4830
  printInfo("");
3958
4831
  }
3959
4832
 
3960
4833
  // src/commands/completion.ts
3961
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync11 } from "fs";
4834
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync12 } from "fs";
3962
4835
  import { resolve as resolve2 } from "path";
3963
4836
  import { homedir as homedir2 } from "os";
3964
4837
 
@@ -4009,14 +4882,14 @@ compdef _clawt_completion clawt
4009
4882
  }
4010
4883
 
4011
4884
  // src/utils/completion-engine.ts
4012
- import { existsSync as existsSync10, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
4013
- import { join as join8, dirname, basename as basename2 } from "path";
4885
+ import { existsSync as existsSync11, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
4886
+ import { join as join9, dirname, basename as basename2 } from "path";
4014
4887
  function completeFilePath(partial) {
4015
4888
  const cwd = process.cwd();
4016
4889
  const hasDir = partial.includes("/");
4017
- const searchDir = hasDir ? join8(cwd, dirname(partial)) : cwd;
4890
+ const searchDir = hasDir ? join9(cwd, dirname(partial)) : cwd;
4018
4891
  const prefix = hasDir ? basename2(partial) : partial;
4019
- if (!existsSync10(searchDir)) {
4892
+ if (!existsSync11(searchDir)) {
4020
4893
  return [];
4021
4894
  }
4022
4895
  const entries = readdirSync6(searchDir);
@@ -4025,7 +4898,7 @@ function completeFilePath(partial) {
4025
4898
  for (const entry of entries) {
4026
4899
  if (!entry.startsWith(prefix)) continue;
4027
4900
  if (entry.startsWith(".")) continue;
4028
- const fullPath = join8(searchDir, entry);
4901
+ const fullPath = join9(searchDir, entry);
4029
4902
  try {
4030
4903
  const stat = statSync5(fullPath);
4031
4904
  if (stat.isDirectory()) {
@@ -4114,15 +4987,15 @@ function generateCompletions(program2, args) {
4114
4987
 
4115
4988
  // src/commands/completion.ts
4116
4989
  function appendToFile(filePath, content) {
4117
- if (existsSync11(filePath)) {
4118
- const current = readFileSync5(filePath, "utf-8");
4990
+ if (existsSync12(filePath)) {
4991
+ const current = readFileSync6(filePath, "utf-8");
4119
4992
  if (current.includes("clawt completion")) {
4120
4993
  printInfo(MESSAGES.COMPLETION_INSTALL_EXISTS + ": " + filePath);
4121
4994
  return;
4122
4995
  }
4123
4996
  content = current + content;
4124
4997
  }
4125
- writeFileSync4(filePath, content, "utf-8");
4998
+ writeFileSync5(filePath, content, "utf-8");
4126
4999
  printSuccess(MESSAGES.COMPLETION_INSTALL_SUCCESS + ": " + filePath);
4127
5000
  printInfo(MESSAGES.COMPLETION_INSTALL_RESTART(filePath));
4128
5001
  }
@@ -4169,6 +5042,37 @@ function registerCompletionCommand(program2) {
4169
5042
  });
4170
5043
  }
4171
5044
 
5045
+ // src/commands/init.ts
5046
+ import { Command as Cmd } from "commander";
5047
+ function registerInitCommand(program2) {
5048
+ const initCmd = program2.command("init").description("\u521D\u59CB\u5316\u9879\u76EE\u7EA7\u914D\u7F6E\uFF0C\u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F").option("-b, --branch <branchName>", "\u6307\u5B9A\u4E3B\u5DE5\u4F5C\u5206\u652F\u540D\uFF08\u9ED8\u8BA4\u4F7F\u7528\u5F53\u524D\u5206\u652F\uFF09").action(async (options) => {
5049
+ await handleInit(options);
5050
+ });
5051
+ initCmd.addCommand(
5052
+ new Cmd("show").description("\u5C55\u793A\u5F53\u524D\u9879\u76EE\u7684 init \u914D\u7F6E").action(() => {
5053
+ handleInitShow();
5054
+ })
5055
+ );
5056
+ }
5057
+ function handleInitShow() {
5058
+ validateMainWorktree();
5059
+ const config2 = requireProjectConfig();
5060
+ const configJson = safeStringify(config2);
5061
+ printInfo(MESSAGES.INIT_SHOW(configJson));
5062
+ }
5063
+ async function handleInit(options) {
5064
+ validateMainWorktree();
5065
+ const existingConfig = loadProjectConfig();
5066
+ const branchName = options.branch || getCurrentBranch();
5067
+ logger.info(`init \u547D\u4EE4\u6267\u884C\uFF0C\u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}`);
5068
+ saveProjectConfig({ clawtMainWorkBranch: branchName });
5069
+ if (existingConfig) {
5070
+ printSuccess(MESSAGES.INIT_UPDATED(existingConfig.clawtMainWorkBranch, branchName));
5071
+ } else {
5072
+ printSuccess(MESSAGES.INIT_SUCCESS(branchName));
5073
+ }
5074
+ }
5075
+
4172
5076
  // src/index.ts
4173
5077
  var require2 = createRequire(import.meta.url);
4174
5078
  var { version } = require2("../package.json");
@@ -4194,6 +5098,7 @@ registerStatusCommand(program);
4194
5098
  registerAliasCommand(program);
4195
5099
  registerProjectsCommand(program);
4196
5100
  registerCompletionCommand(program);
5101
+ registerInitCommand(program);
4197
5102
  var config = loadConfig();
4198
5103
  applyAliases(program, config.aliases);
4199
5104
  process.on("uncaughtException", (error) => {