clawt 2.19.0 → 3.0.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 (75) hide show
  1. package/README.md +12 -2
  2. package/dist/index.js +626 -219
  3. package/dist/postinstall.js +39 -6
  4. package/docs/alias.md +108 -0
  5. package/docs/completion.md +55 -0
  6. package/docs/config-file.md +43 -0
  7. package/docs/config.md +91 -0
  8. package/docs/create.md +85 -0
  9. package/docs/init.md +65 -0
  10. package/docs/list.md +67 -0
  11. package/docs/log.md +67 -0
  12. package/docs/merge.md +137 -0
  13. package/docs/notification.md +94 -0
  14. package/docs/projects.md +135 -0
  15. package/docs/remove.md +79 -0
  16. package/docs/reset.md +35 -0
  17. package/docs/resume.md +99 -0
  18. package/docs/run.md +146 -0
  19. package/docs/spec.md +156 -1879
  20. package/docs/status.md +155 -0
  21. package/docs/sync.md +114 -0
  22. package/docs/update-check.md +95 -0
  23. package/docs/validate.md +368 -0
  24. package/package.json +1 -1
  25. package/src/commands/alias.ts +1 -1
  26. package/src/commands/create.ts +10 -5
  27. package/src/commands/init.ts +75 -0
  28. package/src/commands/list.ts +1 -1
  29. package/src/commands/merge.ts +11 -4
  30. package/src/commands/remove.ts +10 -3
  31. package/src/commands/reset.ts +3 -0
  32. package/src/commands/resume.ts +9 -3
  33. package/src/commands/run.ts +9 -3
  34. package/src/commands/status.ts +1 -1
  35. package/src/commands/sync.ts +18 -6
  36. package/src/commands/validate.ts +46 -52
  37. package/src/constants/branch.ts +3 -0
  38. package/src/constants/config.ts +1 -1
  39. package/src/constants/index.ts +3 -3
  40. package/src/constants/messages/completion.ts +1 -1
  41. package/src/constants/messages/create.ts +3 -0
  42. package/src/constants/messages/index.ts +2 -0
  43. package/src/constants/messages/init.ts +18 -0
  44. package/src/constants/messages/remove.ts +2 -0
  45. package/src/constants/messages/sync.ts +3 -0
  46. package/src/constants/messages/validate.ts +6 -0
  47. package/src/constants/paths.ts +3 -0
  48. package/src/constants/prompt.ts +28 -0
  49. package/src/index.ts +2 -0
  50. package/src/types/command.ts +7 -1
  51. package/src/types/index.ts +2 -1
  52. package/src/types/projectConfig.ts +5 -0
  53. package/src/utils/config.ts +2 -1
  54. package/src/utils/git.ts +18 -0
  55. package/src/utils/index.ts +6 -1
  56. package/src/utils/json.ts +67 -0
  57. package/src/utils/project-config.ts +77 -0
  58. package/src/utils/validate-branch.ts +166 -0
  59. package/src/utils/worktree-matcher.ts +268 -1
  60. package/src/utils/worktree.ts +6 -2
  61. package/tests/unit/commands/create.test.ts +20 -16
  62. package/tests/unit/commands/init.test.ts +146 -0
  63. package/tests/unit/commands/merge.test.ts +7 -1
  64. package/tests/unit/commands/remove.test.ts +4 -0
  65. package/tests/unit/commands/reset.test.ts +2 -0
  66. package/tests/unit/commands/resume.test.ts +29 -8
  67. package/tests/unit/commands/run.test.ts +2 -0
  68. package/tests/unit/commands/sync.test.ts +6 -0
  69. package/tests/unit/commands/validate.test.ts +13 -0
  70. package/tests/unit/utils/config.test.ts +2 -2
  71. package/tests/unit/utils/project-config.test.ts +136 -0
  72. package/tests/unit/utils/update-checker.test.ts +28 -7
  73. package/tests/unit/utils/validate-branch.test.ts +272 -0
  74. package/tests/unit/utils/worktree-matcher.test.ts +142 -1
  75. 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,22 @@ 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
+
437
466
  // src/constants/messages/index.ts
438
467
  var MESSAGES = {
439
468
  ...COMMON_MESSAGES,
@@ -449,7 +478,8 @@ var MESSAGES = {
449
478
  ...STATUS_MESSAGES,
450
479
  ...ALIAS_MESSAGES,
451
480
  ...PROJECTS_MESSAGES,
452
- ...COMPLETION_MESSAGES
481
+ ...COMPLETION_MESSAGES,
482
+ ...INIT_MESSAGES
453
483
  };
454
484
 
455
485
  // src/constants/exitCodes.ts
@@ -567,8 +597,14 @@ var CLEAR_SCREEN = "\x1B[2J";
567
597
  var CURSOR_HOME = "\x1B[H";
568
598
 
569
599
  // src/constants/prompt.ts
600
+ import chalk from "chalk";
570
601
  var SELECT_ALL_NAME = "__select_all__";
571
602
  var SELECT_ALL_LABEL = "[select-all]";
603
+ var GROUP_SELECT_ALL_PREFIX = "__group_select_all_";
604
+ var GROUP_SELECT_ALL_LABEL = (dateLabel) => `[select-all: ${dateLabel}]`;
605
+ 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`;
606
+ var UNKNOWN_DATE_GROUP = "\u672A\u77E5\u65E5\u671F";
607
+ var UNKNOWN_DATE_SEPARATOR_LABEL = `\u2550\u2550\u2550\u2550 ${chalk.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} \u2550\u2550\u2550\u2550`;
572
608
 
573
609
  // src/errors/index.ts
574
610
  var ClawtError = class extends Error {
@@ -588,7 +624,7 @@ var ClawtError = class extends Error {
588
624
  // src/logger/index.ts
589
625
  import winston from "winston";
590
626
  import DailyRotateFile from "winston-daily-rotate-file";
591
- import chalk from "chalk";
627
+ import chalk2 from "chalk";
592
628
  import { existsSync, mkdirSync } from "fs";
593
629
  if (!existsSync(LOGS_DIR)) {
594
630
  mkdirSync(LOGS_DIR, { recursive: true });
@@ -613,13 +649,13 @@ var logger = winston.createLogger({
613
649
  transports: [dailyRotateTransport]
614
650
  });
615
651
  var LEVEL_COLORS = {
616
- error: chalk.red,
617
- warn: chalk.yellow,
618
- info: chalk.cyan,
619
- debug: chalk.gray
652
+ error: chalk2.red,
653
+ warn: chalk2.yellow,
654
+ info: chalk2.cyan,
655
+ debug: chalk2.gray
620
656
  };
621
657
  function colorizeLevel(level) {
622
- const colorFn = LEVEL_COLORS[level] || chalk.white;
658
+ const colorFn = LEVEL_COLORS[level] || chalk2.white;
623
659
  return colorFn(level.toUpperCase().padEnd(5));
624
660
  }
625
661
  function enableConsoleTransport() {
@@ -630,7 +666,7 @@ function enableConsoleTransport() {
630
666
  return;
631
667
  }
632
668
  const consoleFormat = winston.format.printf(({ level, message, timestamp }) => {
633
- return `${chalk.gray(timestamp)} ${colorizeLevel(level)} ${message}`;
669
+ return `${chalk2.gray(timestamp)} ${colorizeLevel(level)} ${message}`;
634
670
  });
635
671
  const consoleTransport = new winston.transports.Console({
636
672
  level: "debug",
@@ -896,24 +932,30 @@ function getBranchCreatedAt(branchName, cwd) {
896
932
  return null;
897
933
  }
898
934
  }
935
+ function gitCheckout(branchName, cwd) {
936
+ execCommand(`git checkout ${branchName}`, { cwd });
937
+ }
938
+ function createBranch(branchName, cwd) {
939
+ execCommand(`git branch ${branchName}`, { cwd });
940
+ }
899
941
 
900
942
  // src/utils/formatter.ts
901
- import chalk2 from "chalk";
943
+ import chalk3 from "chalk";
902
944
  import { createInterface } from "readline";
903
945
  function printSuccess(message) {
904
- console.log(chalk2.green(message));
946
+ console.log(chalk3.green(message));
905
947
  }
906
948
  function printError(message) {
907
- console.error(chalk2.red(`\u2717 ${message}`));
949
+ console.error(chalk3.red(`\u2717 ${message}`));
908
950
  }
909
951
  function printWarning(message) {
910
- console.log(chalk2.yellow(`\u26A0 ${message}`));
952
+ console.log(chalk3.yellow(`\u26A0 ${message}`));
911
953
  }
912
954
  function printInfo(message) {
913
955
  console.log(message);
914
956
  }
915
957
  function printHint(message) {
916
- console.log(chalk2.hex("#FF8C00")(message));
958
+ console.log(chalk3.hex("#FF8C00")(message));
917
959
  }
918
960
  function printSeparator() {
919
961
  console.log(MESSAGES.SEPARATOR);
@@ -934,7 +976,7 @@ function confirmAction(question) {
934
976
  });
935
977
  }
936
978
  function confirmDestructiveAction(dangerousCommand, description) {
937
- printWarning(`\u5373\u5C06\u6267\u884C ${chalk2.red.bold(dangerousCommand)}\uFF0C${description}`);
979
+ printWarning(`\u5373\u5C06\u6267\u884C ${chalk3.red.bold(dangerousCommand)}\uFF0C${description}`);
938
980
  return confirmAction("\u662F\u5426\u7EE7\u7EED\uFF1F");
939
981
  }
940
982
  function isWorktreeIdle(status) {
@@ -942,17 +984,17 @@ function isWorktreeIdle(status) {
942
984
  }
943
985
  function formatWorktreeStatus(status) {
944
986
  const parts = [];
945
- parts.push(chalk2.yellow(`${status.commitCount} \u4E2A\u63D0\u4EA4`));
987
+ parts.push(chalk3.yellow(`${status.commitCount} \u4E2A\u63D0\u4EA4`));
946
988
  if (status.insertions === 0 && status.deletions === 0) {
947
989
  parts.push("\u65E0\u53D8\u66F4");
948
990
  } else {
949
991
  const diffParts = [];
950
- diffParts.push(chalk2.green(`+${status.insertions}`));
951
- diffParts.push(chalk2.red(`-${status.deletions}`));
992
+ diffParts.push(chalk3.green(`+${status.insertions}`));
993
+ diffParts.push(chalk3.red(`-${status.deletions}`));
952
994
  parts.push(diffParts.join(" "));
953
995
  }
954
996
  if (status.hasDirtyFiles) {
955
- parts.push(chalk2.gray("(\u672A\u63D0\u4EA4\u4FEE\u6539)"));
997
+ parts.push(chalk3.gray("(\u672A\u63D0\u4EA4\u4FEE\u6539)"));
956
998
  }
957
999
  return parts.join(" ");
958
1000
  }
@@ -1070,8 +1112,8 @@ function validateClaudeCodeInstalled() {
1070
1112
  }
1071
1113
 
1072
1114
  // src/utils/worktree.ts
1073
- import { join as join3 } from "path";
1074
- import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
1115
+ import { join as join4 } from "path";
1116
+ import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
1075
1117
 
1076
1118
  // src/utils/fs.ts
1077
1119
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmdirSync, statSync } from "fs";
@@ -1112,10 +1154,152 @@ function calculateDirSize(dirPath) {
1112
1154
  return totalSize;
1113
1155
  }
1114
1156
 
1157
+ // src/utils/validate-branch.ts
1158
+ import Enquirer from "enquirer";
1159
+
1160
+ // src/utils/project-config.ts
1161
+ import { existsSync as existsSync3, readFileSync, writeFileSync } from "fs";
1162
+ import { join as join3 } from "path";
1163
+ function getProjectConfigPath(projectName) {
1164
+ return join3(PROJECTS_CONFIG_DIR, projectName, "config.json");
1165
+ }
1166
+ function loadProjectConfig() {
1167
+ const projectName = getProjectName();
1168
+ const configPath = getProjectConfigPath(projectName);
1169
+ if (!existsSync3(configPath)) {
1170
+ return null;
1171
+ }
1172
+ try {
1173
+ const raw = readFileSync(configPath, "utf-8");
1174
+ return JSON.parse(raw);
1175
+ } catch (error) {
1176
+ logger.warn(`\u9879\u76EE\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25: ${error}`);
1177
+ return null;
1178
+ }
1179
+ }
1180
+ function saveProjectConfig(config2) {
1181
+ const projectName = getProjectName();
1182
+ const configPath = getProjectConfigPath(projectName);
1183
+ const projectDir = join3(PROJECTS_CONFIG_DIR, projectName);
1184
+ ensureDir(projectDir);
1185
+ writeFileSync(configPath, JSON.stringify(config2, null, 2), "utf-8");
1186
+ logger.info(`\u9879\u76EE\u914D\u7F6E\u5DF2\u4FDD\u5B58: ${configPath}`);
1187
+ }
1188
+ function requireProjectConfig() {
1189
+ const config2 = loadProjectConfig();
1190
+ if (!config2) {
1191
+ throw new ClawtError(MESSAGES.PROJECT_NOT_INITIALIZED);
1192
+ }
1193
+ if (!config2.clawtMainWorkBranch) {
1194
+ throw new ClawtError(MESSAGES.PROJECT_CONFIG_MISSING_BRANCH);
1195
+ }
1196
+ return config2;
1197
+ }
1198
+ function getMainWorkBranch() {
1199
+ const config2 = requireProjectConfig();
1200
+ return config2.clawtMainWorkBranch;
1201
+ }
1202
+
1203
+ // src/utils/validate-branch.ts
1204
+ function getValidateBranchName(branchName) {
1205
+ return `${VALIDATE_BRANCH_PREFIX}${branchName}`;
1206
+ }
1207
+ function createValidateBranch(branchName, cwd) {
1208
+ const validateBranchName = getValidateBranchName(branchName);
1209
+ if (checkBranchExists(validateBranchName, cwd)) {
1210
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA: ${validateBranchName}`);
1211
+ return;
1212
+ }
1213
+ createBranch(validateBranchName, cwd);
1214
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u5DF2\u521B\u5EFA: ${validateBranchName}`);
1215
+ }
1216
+ function deleteValidateBranch(branchName, cwd) {
1217
+ const validateBranchName = getValidateBranchName(branchName);
1218
+ if (!checkBranchExists(validateBranchName, cwd)) {
1219
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5220\u9664: ${validateBranchName}`);
1220
+ return;
1221
+ }
1222
+ deleteBranch(validateBranchName, cwd);
1223
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u5DF2\u5220\u9664: ${validateBranchName}`);
1224
+ }
1225
+ async function rebuildValidateBranch(branchName, cwd) {
1226
+ const mainBranch = getMainWorkBranch();
1227
+ const currentBranch = getCurrentBranch(cwd);
1228
+ if (currentBranch === mainBranch) {
1229
+ } else if (currentBranch.startsWith(VALIDATE_BRANCH_PREFIX)) {
1230
+ gitResetHard(cwd);
1231
+ gitCleanForce(cwd);
1232
+ gitCheckout(mainBranch, cwd);
1233
+ } else {
1234
+ if (!isWorkingDirClean(cwd)) {
1235
+ await handleDirtyWorkingDir(cwd);
1236
+ }
1237
+ gitCheckout(mainBranch, cwd);
1238
+ }
1239
+ deleteValidateBranch(branchName, cwd);
1240
+ createValidateBranch(branchName, cwd);
1241
+ logger.info(`\u9A8C\u8BC1\u5206\u652F\u5DF2\u91CD\u5EFA: ${getValidateBranchName(branchName)}`);
1242
+ }
1243
+ async function handleDirtyWorkingDir(cwd) {
1244
+ printWarning("\u5F53\u524D\u5206\u652F\u6709\u672A\u63D0\u4EA4\u7684\u66F4\u6539\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F\uFF1A\n");
1245
+ const choice = await new Enquirer.Select({
1246
+ message: "\u9009\u62E9\u5904\u7406\u65B9\u5F0F",
1247
+ choices: [
1248
+ {
1249
+ name: "reset",
1250
+ message: "reset - \u4E22\u5F03\u6240\u6709\u66F4\u6539 (git reset --hard HEAD && git clean -fd)"
1251
+ },
1252
+ {
1253
+ name: "stash",
1254
+ message: "stash - \u6682\u5B58\u66F4\u6539 (git add . && git stash)"
1255
+ },
1256
+ {
1257
+ name: "exit",
1258
+ message: "exit - \u9000\u51FA\uFF0C\u624B\u52A8\u5904\u7406"
1259
+ }
1260
+ ],
1261
+ initial: 0
1262
+ }).run();
1263
+ if (choice === "exit") {
1264
+ throw new ClawtError("\u7528\u6237\u9009\u62E9\u9000\u51FA\uFF0C\u8BF7\u624B\u52A8\u5904\u7406\u5DE5\u4F5C\u533A\u66F4\u6539\u540E\u91CD\u8BD5");
1265
+ }
1266
+ if (choice === "reset") {
1267
+ gitResetHard(cwd);
1268
+ gitCleanForce(cwd);
1269
+ } else if (choice === "stash") {
1270
+ gitAddAll(cwd);
1271
+ gitStashPush("clawt:auto-stash", cwd);
1272
+ }
1273
+ if (!isWorkingDirClean(cwd)) {
1274
+ throw new ClawtError("\u5DE5\u4F5C\u533A\u4ECD\u7136\u4E0D\u5E72\u51C0\uFF0C\u8BF7\u624B\u52A8\u5904\u7406");
1275
+ }
1276
+ }
1277
+ async function ensureOnMainWorkBranch(cwd) {
1278
+ const mainBranch = getMainWorkBranch();
1279
+ const currentBranch = getCurrentBranch(cwd);
1280
+ if (currentBranch === mainBranch) {
1281
+ return;
1282
+ }
1283
+ if (currentBranch.startsWith(VALIDATE_BRANCH_PREFIX)) {
1284
+ logger.info(`\u5F53\u524D\u5728\u9A8C\u8BC1\u5206\u652F ${currentBranch} \u4E0A\uFF0C\u81EA\u52A8\u5207\u56DE\u4E3B\u5DE5\u4F5C\u5206\u652F ${mainBranch}`);
1285
+ if (!isWorkingDirClean(cwd)) {
1286
+ gitResetHard(cwd);
1287
+ gitCleanForce(cwd);
1288
+ }
1289
+ gitCheckout(mainBranch, cwd);
1290
+ return;
1291
+ }
1292
+ logger.info(`\u5F53\u524D\u5728\u5206\u652F ${currentBranch} \u4E0A\uFF0C\u9700\u5207\u6362\u5230\u4E3B\u5DE5\u4F5C\u5206\u652F ${mainBranch}`);
1293
+ if (!isWorkingDirClean(cwd)) {
1294
+ await handleDirtyWorkingDir(cwd);
1295
+ }
1296
+ gitCheckout(mainBranch, cwd);
1297
+ }
1298
+
1115
1299
  // src/utils/worktree.ts
1116
1300
  function getProjectWorktreeDir() {
1117
1301
  const projectName = getProjectName();
1118
- return join3(WORKTREES_DIR, projectName);
1302
+ return join4(WORKTREES_DIR, projectName);
1119
1303
  }
1120
1304
  function createWorktrees(branchName, count) {
1121
1305
  const sanitized = sanitizeBranchName(branchName);
@@ -1125,8 +1309,9 @@ function createWorktrees(branchName, count) {
1125
1309
  ensureDir(projectDir);
1126
1310
  const results = [];
1127
1311
  for (const name of branchNames) {
1128
- const worktreePath = join3(projectDir, name);
1312
+ const worktreePath = join4(projectDir, name);
1129
1313
  createWorktree(name, worktreePath);
1314
+ createValidateBranch(name);
1130
1315
  results.push({ path: worktreePath, branch: name });
1131
1316
  logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
1132
1317
  }
@@ -1138,8 +1323,9 @@ function createWorktreesByBranches(branchNames) {
1138
1323
  ensureDir(projectDir);
1139
1324
  const results = [];
1140
1325
  for (const name of branchNames) {
1141
- const worktreePath = join3(projectDir, name);
1326
+ const worktreePath = join4(projectDir, name);
1142
1327
  createWorktree(name, worktreePath);
1328
+ createValidateBranch(name);
1143
1329
  results.push({ path: worktreePath, branch: name });
1144
1330
  logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
1145
1331
  }
@@ -1147,7 +1333,7 @@ function createWorktreesByBranches(branchNames) {
1147
1333
  }
1148
1334
  function getProjectWorktrees() {
1149
1335
  const projectDir = getProjectWorktreeDir();
1150
- if (!existsSync3(projectDir)) {
1336
+ if (!existsSync4(projectDir)) {
1151
1337
  return [];
1152
1338
  }
1153
1339
  const worktreeListOutput = gitWorktreeList();
@@ -1160,7 +1346,7 @@ function getProjectWorktrees() {
1160
1346
  if (!entry.isDirectory()) {
1161
1347
  continue;
1162
1348
  }
1163
- const fullPath = join3(projectDir, entry.name);
1349
+ const fullPath = join4(projectDir, entry.name);
1164
1350
  if (registeredPaths.has(fullPath)) {
1165
1351
  worktrees.push({
1166
1352
  path: fullPath,
@@ -1175,6 +1361,7 @@ function cleanupWorktrees(worktrees) {
1175
1361
  try {
1176
1362
  removeWorktreeByPath(wt.path);
1177
1363
  deleteBranch(wt.branch);
1364
+ deleteValidateBranch(wt.branch);
1178
1365
  logger.info(`\u5DF2\u6E05\u7406 worktree \u548C\u5206\u652F: ${wt.branch}`);
1179
1366
  } catch (error) {
1180
1367
  logger.error(`\u6E05\u7406 worktree \u5931\u8D25: ${wt.path} - ${error}`);
@@ -1197,13 +1384,13 @@ function getWorktreeStatus(worktree) {
1197
1384
  }
1198
1385
 
1199
1386
  // src/utils/config.ts
1200
- import { existsSync as existsSync4, readFileSync, writeFileSync } from "fs";
1387
+ import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1201
1388
  function loadConfig() {
1202
- if (!existsSync4(CONFIG_PATH)) {
1389
+ if (!existsSync5(CONFIG_PATH)) {
1203
1390
  return { ...DEFAULT_CONFIG };
1204
1391
  }
1205
1392
  try {
1206
- const raw = readFileSync(CONFIG_PATH, "utf-8");
1393
+ const raw = readFileSync2(CONFIG_PATH, "utf-8");
1207
1394
  return { ...DEFAULT_CONFIG, ...JSON.parse(raw) };
1208
1395
  } catch {
1209
1396
  logger.warn(MESSAGES.CONFIG_CORRUPTED);
@@ -1212,13 +1399,13 @@ function loadConfig() {
1212
1399
  }
1213
1400
  }
1214
1401
  function writeConfig(config2) {
1215
- writeFileSync(CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
1402
+ writeFileSync2(CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
1216
1403
  }
1217
1404
  function writeDefaultConfig() {
1218
1405
  writeConfig(DEFAULT_CONFIG);
1219
1406
  }
1220
1407
  function saveConfig(config2) {
1221
- writeFileSync(CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
1408
+ writeFileSync2(CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
1222
1409
  }
1223
1410
  function getConfigValue(key) {
1224
1411
  const config2 = loadConfig();
@@ -1228,6 +1415,7 @@ function ensureClawtDirs() {
1228
1415
  ensureDir(CLAWT_HOME);
1229
1416
  ensureDir(LOGS_DIR);
1230
1417
  ensureDir(WORKTREES_DIR);
1418
+ ensureDir(PROJECTS_CONFIG_DIR);
1231
1419
  }
1232
1420
  function parseConcurrency(optionValue, configValue) {
1233
1421
  if (optionValue === void 0) {
@@ -1241,18 +1429,18 @@ function parseConcurrency(optionValue, configValue) {
1241
1429
  }
1242
1430
 
1243
1431
  // src/utils/prompt.ts
1244
- import Enquirer from "enquirer";
1432
+ import Enquirer2 from "enquirer";
1245
1433
 
1246
1434
  // src/utils/claude.ts
1247
1435
  import { spawnSync as spawnSync2 } from "child_process";
1248
- import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
1249
- import { join as join4 } from "path";
1436
+ import { existsSync as existsSync7, readdirSync as readdirSync3 } from "fs";
1437
+ import { join as join5 } from "path";
1250
1438
 
1251
1439
  // src/utils/terminal.ts
1252
1440
  import { execFileSync as execFileSync2 } from "child_process";
1253
- import { existsSync as existsSync5 } from "fs";
1441
+ import { existsSync as existsSync6 } from "fs";
1254
1442
  function isITerm2Installed() {
1255
- return existsSync5(ITERM2_APP_PATH);
1443
+ return existsSync6(ITERM2_APP_PATH);
1256
1444
  }
1257
1445
  function detectTerminalApp() {
1258
1446
  const configured = getConfigValue("terminalApp");
@@ -1325,8 +1513,8 @@ function encodeClaudeProjectPath(absolutePath) {
1325
1513
  }
1326
1514
  function hasClaudeSessionHistory(worktreePath) {
1327
1515
  const encodedName = encodeClaudeProjectPath(worktreePath);
1328
- const projectDir = join4(CLAUDE_PROJECTS_DIR, encodedName);
1329
- if (!existsSync6(projectDir)) {
1516
+ const projectDir = join5(CLAUDE_PROJECTS_DIR, encodedName);
1517
+ if (!existsSync7(projectDir)) {
1330
1518
  return false;
1331
1519
  }
1332
1520
  const entries = readdirSync3(projectDir);
@@ -1382,20 +1570,20 @@ function launchInteractiveClaudeInNewTerminal(worktree, hasPreviousSession) {
1382
1570
  }
1383
1571
 
1384
1572
  // src/utils/validate-snapshot.ts
1385
- import { join as join5 } from "path";
1386
- import { existsSync as existsSync7, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync, readdirSync as readdirSync4, rmdirSync as rmdirSync2, statSync as statSync2 } from "fs";
1573
+ import { join as join6 } from "path";
1574
+ import { existsSync as existsSync8, readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync, readdirSync as readdirSync4, rmdirSync as rmdirSync2, statSync as statSync2 } from "fs";
1387
1575
  function getSnapshotPath(projectName, branchName) {
1388
- return join5(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
1576
+ return join6(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
1389
1577
  }
1390
1578
  function getSnapshotHeadPath(projectName, branchName) {
1391
- return join5(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
1579
+ return join6(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
1392
1580
  }
1393
1581
  function hasSnapshot(projectName, branchName) {
1394
- return existsSync7(getSnapshotPath(projectName, branchName));
1582
+ return existsSync8(getSnapshotPath(projectName, branchName));
1395
1583
  }
1396
1584
  function getSnapshotModifiedTime(projectName, branchName) {
1397
1585
  const snapshotPath = getSnapshotPath(projectName, branchName);
1398
- if (!existsSync7(snapshotPath)) return null;
1586
+ if (!existsSync8(snapshotPath)) return null;
1399
1587
  const stat = statSync2(snapshotPath);
1400
1588
  return stat.mtime.toISOString();
1401
1589
  }
@@ -1403,47 +1591,47 @@ function readSnapshot(projectName, branchName) {
1403
1591
  const snapshotPath = getSnapshotPath(projectName, branchName);
1404
1592
  const headPath = getSnapshotHeadPath(projectName, branchName);
1405
1593
  logger.debug(`\u8BFB\u53D6 validate \u5FEB\u7167: ${snapshotPath}`);
1406
- const treeHash = existsSync7(snapshotPath) ? readFileSync2(snapshotPath, "utf-8").trim() : "";
1407
- const headCommitHash = existsSync7(headPath) ? readFileSync2(headPath, "utf-8").trim() : "";
1594
+ const treeHash = existsSync8(snapshotPath) ? readFileSync3(snapshotPath, "utf-8").trim() : "";
1595
+ const headCommitHash = existsSync8(headPath) ? readFileSync3(headPath, "utf-8").trim() : "";
1408
1596
  return { treeHash, headCommitHash };
1409
1597
  }
1410
1598
  function writeSnapshot(projectName, branchName, treeHash, headCommitHash) {
1411
1599
  const snapshotPath = getSnapshotPath(projectName, branchName);
1412
1600
  const headPath = getSnapshotHeadPath(projectName, branchName);
1413
- const snapshotDir = join5(VALIDATE_SNAPSHOTS_DIR, projectName);
1601
+ const snapshotDir = join6(VALIDATE_SNAPSHOTS_DIR, projectName);
1414
1602
  ensureDir(snapshotDir);
1415
- writeFileSync2(snapshotPath, treeHash, "utf-8");
1416
- writeFileSync2(headPath, headCommitHash, "utf-8");
1603
+ writeFileSync3(snapshotPath, treeHash, "utf-8");
1604
+ writeFileSync3(headPath, headCommitHash, "utf-8");
1417
1605
  logger.info(`\u5DF2\u4FDD\u5B58 validate \u5FEB\u7167: ${snapshotPath}, ${headPath}`);
1418
1606
  }
1419
1607
  function removeSnapshot(projectName, branchName) {
1420
1608
  const snapshotPath = getSnapshotPath(projectName, branchName);
1421
1609
  const headPath = getSnapshotHeadPath(projectName, branchName);
1422
- if (existsSync7(snapshotPath)) {
1610
+ if (existsSync8(snapshotPath)) {
1423
1611
  unlinkSync(snapshotPath);
1424
1612
  logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${snapshotPath}`);
1425
1613
  }
1426
- if (existsSync7(headPath)) {
1614
+ if (existsSync8(headPath)) {
1427
1615
  unlinkSync(headPath);
1428
1616
  logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${headPath}`);
1429
1617
  }
1430
1618
  }
1431
1619
  function getProjectSnapshotBranches(projectName) {
1432
- const projectDir = join5(VALIDATE_SNAPSHOTS_DIR, projectName);
1433
- if (!existsSync7(projectDir)) {
1620
+ const projectDir = join6(VALIDATE_SNAPSHOTS_DIR, projectName);
1621
+ if (!existsSync8(projectDir)) {
1434
1622
  return [];
1435
1623
  }
1436
1624
  const files = readdirSync4(projectDir);
1437
1625
  return files.filter((f) => f.endsWith(".tree")).map((f) => f.replace(/\.tree$/, ""));
1438
1626
  }
1439
1627
  function removeProjectSnapshots(projectName) {
1440
- const projectDir = join5(VALIDATE_SNAPSHOTS_DIR, projectName);
1441
- if (!existsSync7(projectDir)) {
1628
+ const projectDir = join6(VALIDATE_SNAPSHOTS_DIR, projectName);
1629
+ if (!existsSync8(projectDir)) {
1442
1630
  return;
1443
1631
  }
1444
1632
  const files = readdirSync4(projectDir);
1445
1633
  for (const file of files) {
1446
- unlinkSync(join5(projectDir, file));
1634
+ unlinkSync(join6(projectDir, file));
1447
1635
  }
1448
1636
  try {
1449
1637
  rmdirSync2(projectDir);
@@ -1453,7 +1641,8 @@ function removeProjectSnapshots(projectName) {
1453
1641
  }
1454
1642
 
1455
1643
  // src/utils/worktree-matcher.ts
1456
- import Enquirer2 from "enquirer";
1644
+ import Enquirer3 from "enquirer";
1645
+ import { statSync as statSync3 } from "fs";
1457
1646
  function findExactMatch(worktrees, branchName) {
1458
1647
  return worktrees.find((wt) => wt.branch === branchName);
1459
1648
  }
@@ -1462,7 +1651,7 @@ function findFuzzyMatches(worktrees, keyword) {
1462
1651
  return worktrees.filter((wt) => wt.branch.toLowerCase().includes(lowerKeyword));
1463
1652
  }
1464
1653
  async function promptSelectBranch(worktrees, message) {
1465
- const selectedBranch = await new Enquirer2.Select({
1654
+ const selectedBranch = await new Enquirer3.Select({
1466
1655
  message,
1467
1656
  choices: worktrees.map((wt) => ({
1468
1657
  name: wt.branch,
@@ -1480,7 +1669,7 @@ async function promptMultiSelectBranches(worktrees, message) {
1480
1669
  { name: SELECT_ALL_NAME, message: SELECT_ALL_LABEL },
1481
1670
  ...branchChoices
1482
1671
  ];
1483
- const MultiSelect = Enquirer2.MultiSelect;
1672
+ const MultiSelect = Enquirer3.MultiSelect;
1484
1673
  class MultiSelectWithSelectAll extends MultiSelect {
1485
1674
  space() {
1486
1675
  if (!this.focused) return;
@@ -1558,9 +1747,142 @@ async function resolveTargetWorktree(worktrees, messages, branchName) {
1558
1747
  const allBranches = worktrees.map((wt) => wt.branch);
1559
1748
  throw new ClawtError(messages.noMatch(branchName, allBranches));
1560
1749
  }
1750
+ function formatLocalDate(date) {
1751
+ const year = date.getFullYear();
1752
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1753
+ const day = String(date.getDate()).padStart(2, "0");
1754
+ return `${year}-${month}-${day}`;
1755
+ }
1756
+ function getWorktreeCreatedDate(dirPath) {
1757
+ try {
1758
+ const stat = statSync3(dirPath);
1759
+ return formatLocalDate(stat.birthtime);
1760
+ } catch {
1761
+ return null;
1762
+ }
1763
+ }
1764
+ function formatRelativeDate(dateStr) {
1765
+ const today = formatLocalDate(/* @__PURE__ */ new Date());
1766
+ const todayMs = new Date(today).getTime();
1767
+ const targetMs = new Date(dateStr).getTime();
1768
+ const diffDays = Math.round((todayMs - targetMs) / (1e3 * 60 * 60 * 24));
1769
+ if (diffDays === 0) return "\u4ECA\u5929";
1770
+ if (diffDays === 1) return "\u6628\u5929";
1771
+ if (diffDays < 30) return `${diffDays} \u5929\u524D`;
1772
+ if (diffDays < 365) {
1773
+ const months = Math.floor(diffDays / 30);
1774
+ return `${months} \u4E2A\u6708\u524D`;
1775
+ }
1776
+ const years = Math.floor(diffDays / 365);
1777
+ return `${years} \u5E74\u524D`;
1778
+ }
1779
+ function groupWorktreesByDate(worktrees) {
1780
+ const groups = /* @__PURE__ */ new Map();
1781
+ for (const wt of worktrees) {
1782
+ const dateKey = getWorktreeCreatedDate(wt.path) ?? UNKNOWN_DATE_GROUP;
1783
+ if (!groups.has(dateKey)) {
1784
+ groups.set(dateKey, []);
1785
+ }
1786
+ groups.get(dateKey).push(wt);
1787
+ }
1788
+ const sortedEntries = [...groups.entries()].sort((a, b) => {
1789
+ if (a[0] === UNKNOWN_DATE_GROUP) return 1;
1790
+ if (b[0] === UNKNOWN_DATE_GROUP) return -1;
1791
+ return b[0].localeCompare(a[0]);
1792
+ });
1793
+ return new Map(sortedEntries);
1794
+ }
1795
+ function buildGroupedChoices(groups) {
1796
+ const choices = [];
1797
+ choices.push({ name: SELECT_ALL_NAME, message: SELECT_ALL_LABEL });
1798
+ for (const [dateKey, worktreeList] of groups) {
1799
+ if (dateKey === UNKNOWN_DATE_GROUP) {
1800
+ choices.push({ role: "separator", message: UNKNOWN_DATE_SEPARATOR_LABEL });
1801
+ } else {
1802
+ const relativeTime = formatRelativeDate(dateKey);
1803
+ choices.push({ role: "separator", message: GROUP_SEPARATOR_LABEL(dateKey, relativeTime) });
1804
+ }
1805
+ const groupSelectAllName = `${GROUP_SELECT_ALL_PREFIX}${dateKey}`;
1806
+ choices.push({ name: groupSelectAllName, message: GROUP_SELECT_ALL_LABEL(dateKey) });
1807
+ for (const wt of worktreeList) {
1808
+ choices.push({ name: wt.branch, message: wt.branch });
1809
+ }
1810
+ }
1811
+ return choices;
1812
+ }
1813
+ function buildGroupMembershipMap(groups) {
1814
+ const map = /* @__PURE__ */ new Map();
1815
+ for (const [dateKey, worktreeList] of groups) {
1816
+ const groupSelectAllName = `${GROUP_SELECT_ALL_PREFIX}${dateKey}`;
1817
+ map.set(groupSelectAllName, worktreeList.map((wt) => wt.branch));
1818
+ }
1819
+ return map;
1820
+ }
1821
+ async function promptGroupedMultiSelectBranches(worktrees, message) {
1822
+ const groups = groupWorktreesByDate(worktrees);
1823
+ const choices = buildGroupedChoices(groups);
1824
+ const groupMembershipMap = buildGroupMembershipMap(groups);
1825
+ const groupSelectAllNames = new Set(groupMembershipMap.keys());
1826
+ const allBranchNames = new Set(worktrees.map((wt) => wt.branch));
1827
+ const MultiSelect = Enquirer3.MultiSelect;
1828
+ class MultiSelectWithGroupSelectAll extends MultiSelect {
1829
+ space() {
1830
+ if (!this.focused) return;
1831
+ const focusedName = this.focused.name;
1832
+ if (focusedName === SELECT_ALL_NAME) {
1833
+ const willEnable = !this.focused.enabled;
1834
+ for (const ch of this.choices) {
1835
+ ch.enabled = willEnable;
1836
+ }
1837
+ return this.render();
1838
+ }
1839
+ if (groupSelectAllNames.has(focusedName)) {
1840
+ const willEnable = !this.focused.enabled;
1841
+ const memberNames = groupMembershipMap.get(focusedName);
1842
+ this.focused.enabled = willEnable;
1843
+ for (const ch of this.choices) {
1844
+ if (memberNames.includes(ch.name)) {
1845
+ ch.enabled = willEnable;
1846
+ }
1847
+ }
1848
+ syncGlobalSelectAll(this.choices);
1849
+ return this.render();
1850
+ }
1851
+ this.toggle(this.focused);
1852
+ syncGroupSelectAll(this.choices, focusedName);
1853
+ syncGlobalSelectAll(this.choices);
1854
+ return this.render();
1855
+ }
1856
+ }
1857
+ function syncGlobalSelectAll(choiceList) {
1858
+ const selectAllChoice = choiceList.find((ch) => ch.name === SELECT_ALL_NAME);
1859
+ if (!selectAllChoice) return;
1860
+ const branchItems = choiceList.filter((ch) => allBranchNames.has(ch.name));
1861
+ selectAllChoice.enabled = branchItems.length > 0 && branchItems.every((ch) => ch.enabled);
1862
+ }
1863
+ function syncGroupSelectAll(choiceList, branchName) {
1864
+ for (const [groupName, memberNames] of groupMembershipMap) {
1865
+ if (!memberNames.includes(branchName)) continue;
1866
+ const groupChoice = choiceList.find((ch) => ch.name === groupName);
1867
+ if (!groupChoice) continue;
1868
+ const memberChoices = choiceList.filter((ch) => memberNames.includes(ch.name));
1869
+ groupChoice.enabled = memberChoices.length > 0 && memberChoices.every((ch) => ch.enabled);
1870
+ break;
1871
+ }
1872
+ }
1873
+ const selectedBranches = await new MultiSelectWithGroupSelectAll({
1874
+ message,
1875
+ choices,
1876
+ // 使用空心圆/实心圆作为选中指示符
1877
+ symbols: {
1878
+ indicator: { on: "\u25CF", off: "\u25CB" }
1879
+ }
1880
+ }).run();
1881
+ return worktrees.filter((wt) => selectedBranches.includes(wt.branch));
1882
+ }
1561
1883
 
1562
1884
  // src/utils/progress-render.ts
1563
- import chalk3 from "chalk";
1885
+ import chalk4 from "chalk";
1564
1886
  import stringWidth from "string-width";
1565
1887
  var ANSI_RESET = "\x1B[0m";
1566
1888
  function truncateToTerminalWidth(text, maxWidth) {
@@ -1597,23 +1919,23 @@ function renderTaskLine(task, total, maxPathWidth, spinnerChar) {
1597
1919
  const pathStr = task.path.padEnd(maxPathWidth);
1598
1920
  switch (task.status) {
1599
1921
  case "pending": {
1600
- return `${indexStr} ${pathStr} ${chalk3.gray(TASK_STATUS_ICONS.PENDING)} ${chalk3.gray(TASK_STATUS_LABELS.PENDING)}`;
1922
+ return `${indexStr} ${pathStr} ${chalk4.gray(TASK_STATUS_ICONS.PENDING)} ${chalk4.gray(TASK_STATUS_LABELS.PENDING)}`;
1601
1923
  }
1602
1924
  case "running": {
1603
1925
  const elapsed = formatDuration(Date.now() - task.startedAt);
1604
- const detail = task.activity ? ` ${chalk3.dim(task.activity)}` : "";
1605
- return `${indexStr} ${pathStr} ${chalk3.cyan(spinnerChar)} ${chalk3.cyan(TASK_STATUS_LABELS.RUNNING)} ${chalk3.gray(elapsed)}${detail}`;
1926
+ const detail = task.activity ? ` ${chalk4.dim(task.activity)}` : "";
1927
+ return `${indexStr} ${pathStr} ${chalk4.cyan(spinnerChar)} ${chalk4.cyan(TASK_STATUS_LABELS.RUNNING)} ${chalk4.gray(elapsed)}${detail}`;
1606
1928
  }
1607
1929
  case "done": {
1608
1930
  const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
1609
1931
  const cost = task.costUsd != null ? `$${task.costUsd.toFixed(2)}` : "";
1610
- const preview = task.resultPreview ? ` ${chalk3.dim(task.resultPreview)}` : "";
1611
- return `${indexStr} ${pathStr} ${chalk3.green(TASK_STATUS_ICONS.DONE)} ${chalk3.green(TASK_STATUS_LABELS.DONE)} ${chalk3.gray(duration)} ${chalk3.yellow(cost)}${preview}`;
1932
+ const preview = task.resultPreview ? ` ${chalk4.dim(task.resultPreview)}` : "";
1933
+ return `${indexStr} ${pathStr} ${chalk4.green(TASK_STATUS_ICONS.DONE)} ${chalk4.green(TASK_STATUS_LABELS.DONE)} ${chalk4.gray(duration)} ${chalk4.yellow(cost)}${preview}`;
1612
1934
  }
1613
1935
  case "failed": {
1614
1936
  const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
1615
- const preview = task.resultPreview ? ` ${chalk3.dim(task.resultPreview)}` : "";
1616
- return `${indexStr} ${pathStr} ${chalk3.red(TASK_STATUS_ICONS.FAILED)} ${chalk3.red(TASK_STATUS_LABELS.FAILED)} ${chalk3.gray(duration)}${preview}`;
1937
+ const preview = task.resultPreview ? ` ${chalk4.dim(task.resultPreview)}` : "";
1938
+ return `${indexStr} ${pathStr} ${chalk4.red(TASK_STATUS_ICONS.FAILED)} ${chalk4.red(TASK_STATUS_LABELS.FAILED)} ${chalk4.gray(duration)}${preview}`;
1617
1939
  }
1618
1940
  }
1619
1941
  }
@@ -1623,10 +1945,10 @@ function renderSummaryLine(tasks, total) {
1623
1945
  const failed = tasks.filter((t) => t.status === "failed").length;
1624
1946
  const pending = tasks.filter((t) => t.status === "pending").length;
1625
1947
  const parts = [];
1626
- if (running > 0) parts.push(chalk3.cyan(`${running}/${total} ${TASK_STATUS_LABELS.RUNNING}`));
1627
- if (done > 0) parts.push(chalk3.green(`${done}/${total} ${TASK_STATUS_LABELS.DONE}`));
1628
- if (failed > 0) parts.push(chalk3.red(`${failed}/${total} ${TASK_STATUS_LABELS.FAILED}`));
1629
- if (pending > 0) parts.push(chalk3.gray(`${pending}/${total} ${TASK_STATUS_LABELS.PENDING}`));
1948
+ if (running > 0) parts.push(chalk4.cyan(`${running}/${total} ${TASK_STATUS_LABELS.RUNNING}`));
1949
+ if (done > 0) parts.push(chalk4.green(`${done}/${total} ${TASK_STATUS_LABELS.DONE}`));
1950
+ if (failed > 0) parts.push(chalk4.red(`${failed}/${total} ${TASK_STATUS_LABELS.FAILED}`));
1951
+ if (pending > 0) parts.push(chalk4.gray(`${pending}/${total} ${TASK_STATUS_LABELS.PENDING}`));
1630
1952
  return `[${parts.join(", ")}]`;
1631
1953
  }
1632
1954
 
@@ -1858,7 +2180,7 @@ var ProgressRenderer = class {
1858
2180
 
1859
2181
  // src/utils/task-file.ts
1860
2182
  import { resolve } from "path";
1861
- import { existsSync as existsSync8, readFileSync as readFileSync3 } from "fs";
2183
+ import { existsSync as existsSync9, readFileSync as readFileSync4 } from "fs";
1862
2184
  var TASK_BLOCK_REGEX = /<!-- CLAWT-TASKS:START -->([\s\S]*?)<!-- CLAWT-TASKS:END -->/g;
1863
2185
  var BRANCH_LINE_REGEX = /^#\s*branch:\s*(.+)$/;
1864
2186
  var EMPTY_TASKS_MESSAGE = "\u4EFB\u52A1\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A";
@@ -1904,10 +2226,10 @@ function parseTaskFile(content, options) {
1904
2226
  }
1905
2227
  function loadTaskFile(filePath, options) {
1906
2228
  const absolutePath = resolve(filePath);
1907
- if (!existsSync8(absolutePath)) {
2229
+ if (!existsSync9(absolutePath)) {
1908
2230
  throw new ClawtError(MESSAGES.TASK_FILE_NOT_FOUND(absolutePath));
1909
2231
  }
1910
- const content = readFileSync3(absolutePath, "utf-8");
2232
+ const content = readFileSync4(absolutePath, "utf-8");
1911
2233
  const entries = parseTaskFile(content, options);
1912
2234
  if (entries.length === 0) {
1913
2235
  throw new ClawtError(MESSAGES.TASK_FILE_EMPTY);
@@ -2241,8 +2563,8 @@ async function executeBatchTasks(worktrees, tasks, concurrency) {
2241
2563
  }
2242
2564
 
2243
2565
  // src/utils/dry-run.ts
2244
- import chalk4 from "chalk";
2245
- import { join as join6 } from "path";
2566
+ import chalk5 from "chalk";
2567
+ import { join as join7 } from "path";
2246
2568
  var DRY_RUN_TASK_DESC_MAX_LENGTH = 80;
2247
2569
  function truncateTaskDesc(task) {
2248
2570
  const oneLine = task.replace(/\n/g, " ").trim();
@@ -2255,7 +2577,7 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
2255
2577
  const projectDir = getProjectWorktreeDir();
2256
2578
  const isInteractive = tasks.length === 0;
2257
2579
  printDoubleSeparator();
2258
- printInfo(` ${chalk4.bold(MESSAGES.DRY_RUN_TITLE)}`);
2580
+ printInfo(` ${chalk5.bold(MESSAGES.DRY_RUN_TITLE)}`);
2259
2581
  printDoubleSeparator();
2260
2582
  const summaryParts = [
2261
2583
  MESSAGES.DRY_RUN_TASK_COUNT(branchNames.length),
@@ -2265,31 +2587,31 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
2265
2587
  if (isInteractive) {
2266
2588
  summaryParts.push(MESSAGES.DRY_RUN_INTERACTIVE_MODE);
2267
2589
  }
2268
- printInfo(summaryParts.join(chalk4.gray(" \u2502 ")));
2590
+ printInfo(summaryParts.join(chalk5.gray(" \u2502 ")));
2269
2591
  printSeparator();
2270
2592
  let hasConflict = false;
2271
2593
  for (let i = 0; i < branchNames.length; i++) {
2272
2594
  const branch = branchNames[i];
2273
- const worktreePath = join6(projectDir, branch);
2595
+ const worktreePath = join7(projectDir, branch);
2274
2596
  const exists = checkBranchExists(branch);
2275
2597
  if (exists) hasConflict = true;
2276
2598
  const indexLabel = `[${i + 1}/${branchNames.length}]`;
2277
2599
  if (exists) {
2278
- printInfo(`${chalk4.yellow("\u26A0")} ${indexLabel} ${chalk4.yellow(branch)} ${chalk4.gray("\u2014")} ${chalk4.yellow(MESSAGES.DRY_RUN_BRANCH_EXISTS_WARNING(branch))}`);
2600
+ printInfo(`${chalk5.yellow("\u26A0")} ${indexLabel} ${chalk5.yellow(branch)} ${chalk5.gray("\u2014")} ${chalk5.yellow(MESSAGES.DRY_RUN_BRANCH_EXISTS_WARNING(branch))}`);
2279
2601
  } else {
2280
- printInfo(`${chalk4.green("\u2713")} ${indexLabel} ${chalk4.cyan(branch)}`);
2602
+ printInfo(`${chalk5.green("\u2713")} ${indexLabel} ${chalk5.cyan(branch)}`);
2281
2603
  }
2282
- printInfo(` ${chalk4.gray("\u8DEF\u5F84:")} ${worktreePath}`);
2604
+ printInfo(` ${chalk5.gray("\u8DEF\u5F84:")} ${worktreePath}`);
2283
2605
  if (!isInteractive) {
2284
- printInfo(` ${chalk4.gray("\u4EFB\u52A1:")} ${truncateTaskDesc(tasks[i])}`);
2606
+ printInfo(` ${chalk5.gray("\u4EFB\u52A1:")} ${truncateTaskDesc(tasks[i])}`);
2285
2607
  }
2286
2608
  printInfo("");
2287
2609
  }
2288
2610
  printDoubleSeparator();
2289
2611
  if (hasConflict) {
2290
- printInfo(chalk4.yellow(`\u26A0 ${MESSAGES.DRY_RUN_HAS_CONFLICT}`));
2612
+ printInfo(chalk5.yellow(`\u26A0 ${MESSAGES.DRY_RUN_HAS_CONFLICT}`));
2291
2613
  } else {
2292
- printInfo(chalk4.green(`\u2713 ${MESSAGES.DRY_RUN_READY}`));
2614
+ printInfo(chalk5.green(`\u2713 ${MESSAGES.DRY_RUN_READY}`));
2293
2615
  }
2294
2616
  }
2295
2617
 
@@ -2307,8 +2629,8 @@ function applyAliases(program2, aliases) {
2307
2629
  }
2308
2630
 
2309
2631
  // src/utils/config-strategy.ts
2310
- import chalk5 from "chalk";
2311
- import Enquirer3 from "enquirer";
2632
+ import chalk6 from "chalk";
2633
+ import Enquirer4 from "enquirer";
2312
2634
  function isValidConfigKey(key) {
2313
2635
  return key in DEFAULT_CONFIG;
2314
2636
  }
@@ -2351,16 +2673,16 @@ async function promptConfigValue(key, currentValue) {
2351
2673
  }
2352
2674
  function formatConfigValue(value) {
2353
2675
  if (typeof value === "boolean") {
2354
- return value ? chalk5.green("true") : chalk5.yellow("false");
2676
+ return value ? chalk6.green("true") : chalk6.yellow("false");
2355
2677
  }
2356
- return chalk5.cyan(String(value));
2678
+ return chalk6.cyan(String(value));
2357
2679
  }
2358
2680
  async function promptBooleanValue(key, currentValue) {
2359
2681
  const choices = [
2360
2682
  { name: "true", message: "true" },
2361
2683
  { name: "false", message: "false" }
2362
2684
  ];
2363
- const selected = await new Enquirer3.Select({
2685
+ const selected = await new Enquirer4.Select({
2364
2686
  message: MESSAGES.CONFIG_INPUT_PROMPT(key),
2365
2687
  choices,
2366
2688
  initial: currentValue ? 0 : 1
@@ -2368,7 +2690,7 @@ async function promptBooleanValue(key, currentValue) {
2368
2690
  return selected === "true";
2369
2691
  }
2370
2692
  async function promptNumberValue(key, currentValue) {
2371
- const input = await new Enquirer3.Input({
2693
+ const input = await new Enquirer4.Input({
2372
2694
  message: MESSAGES.CONFIG_INPUT_PROMPT(key),
2373
2695
  initial: String(currentValue),
2374
2696
  validate: (val) => {
@@ -2383,28 +2705,28 @@ async function promptEnumValue(key, currentValue, allowedValues) {
2383
2705
  name: v,
2384
2706
  message: v
2385
2707
  }));
2386
- return await new Enquirer3.Select({
2708
+ return await new Enquirer4.Select({
2387
2709
  message: MESSAGES.CONFIG_INPUT_PROMPT(key),
2388
2710
  choices,
2389
2711
  initial: allowedValues.indexOf(currentValue)
2390
2712
  }).run();
2391
2713
  }
2392
2714
  async function promptStringValue(key, currentValue) {
2393
- return await new Enquirer3.Input({
2715
+ return await new Enquirer4.Input({
2394
2716
  message: MESSAGES.CONFIG_INPUT_PROMPT(key),
2395
2717
  initial: currentValue
2396
2718
  }).run();
2397
2719
  }
2398
2720
 
2399
2721
  // src/utils/update-checker.ts
2400
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2722
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
2401
2723
  import { execSync as execSync3 } from "child_process";
2402
2724
  import { request } from "https";
2403
- import chalk6 from "chalk";
2725
+ import chalk7 from "chalk";
2404
2726
  import stringWidth2 from "string-width";
2405
2727
  function readUpdateCache() {
2406
2728
  try {
2407
- const raw = readFileSync4(UPDATE_CHECK_PATH, "utf-8");
2729
+ const raw = readFileSync5(UPDATE_CHECK_PATH, "utf-8");
2408
2730
  return JSON.parse(raw);
2409
2731
  } catch {
2410
2732
  return null;
@@ -2412,7 +2734,7 @@ function readUpdateCache() {
2412
2734
  }
2413
2735
  function writeUpdateCache(cache) {
2414
2736
  try {
2415
- writeFileSync3(UPDATE_CHECK_PATH, JSON.stringify(cache, null, 2), "utf-8");
2737
+ writeFileSync4(UPDATE_CHECK_PATH, JSON.stringify(cache, null, 2), "utf-8");
2416
2738
  } catch {
2417
2739
  }
2418
2740
  }
@@ -2475,12 +2797,12 @@ function detectPackageManager() {
2475
2797
  }
2476
2798
  function printUpdateNotification(currentVersion, latestVersion) {
2477
2799
  const updateText = UPDATE_MESSAGES.UPDATE_AVAILABLE(
2478
- chalk6.red(currentVersion),
2479
- chalk6.green(latestVersion)
2800
+ chalk7.red(currentVersion),
2801
+ chalk7.green(latestVersion)
2480
2802
  );
2481
2803
  const pm = detectPackageManager();
2482
2804
  const updateCommand = UPDATE_COMMANDS[pm] || UPDATE_COMMANDS.npm;
2483
- const commandText = UPDATE_MESSAGES.UPDATE_HINT(chalk6.cyan(updateCommand));
2805
+ const commandText = UPDATE_MESSAGES.UPDATE_HINT(chalk7.cyan(updateCommand));
2484
2806
  const updateTextWidth = stringWidth2(updateText);
2485
2807
  const commandTextWidth = stringWidth2(commandText);
2486
2808
  const innerWidth = Math.max(updateTextWidth, commandTextWidth) + 4;
@@ -2527,10 +2849,60 @@ async function checkForUpdates(currentVersion) {
2527
2849
  }
2528
2850
  }
2529
2851
 
2852
+ // src/utils/json.ts
2853
+ function primitiveToString(value) {
2854
+ if (value === void 0) {
2855
+ return "undefined";
2856
+ }
2857
+ if (value === null) {
2858
+ return "null";
2859
+ }
2860
+ if (typeof value === "symbol") {
2861
+ return value.toString();
2862
+ }
2863
+ if (typeof value === "function") {
2864
+ return `[Function: ${value.name || "anonymous"}]`;
2865
+ }
2866
+ return String(value);
2867
+ }
2868
+ function safeStringify(value, indent = 2) {
2869
+ if (value === null || typeof value !== "object") {
2870
+ return primitiveToString(value);
2871
+ }
2872
+ try {
2873
+ const seen = /* @__PURE__ */ new WeakSet();
2874
+ return JSON.stringify(
2875
+ value,
2876
+ (_key, val) => {
2877
+ if (typeof val === "bigint") {
2878
+ return val.toString();
2879
+ }
2880
+ if (typeof val === "undefined" || typeof val === "function" || typeof val === "symbol") {
2881
+ return primitiveToString(val);
2882
+ }
2883
+ if (typeof val === "object" && val !== null) {
2884
+ if (seen.has(val)) {
2885
+ return "[Circular]";
2886
+ }
2887
+ seen.add(val);
2888
+ }
2889
+ return val;
2890
+ },
2891
+ indent
2892
+ );
2893
+ } catch {
2894
+ try {
2895
+ return JSON.stringify(String(value), null, indent);
2896
+ } catch {
2897
+ return "[Unserializable]";
2898
+ }
2899
+ }
2900
+ }
2901
+
2530
2902
  // src/commands/list.ts
2531
- import chalk7 from "chalk";
2903
+ import chalk8 from "chalk";
2532
2904
  function registerListCommand(program2) {
2533
- program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
2905
+ 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) => {
2534
2906
  handleList(options);
2535
2907
  });
2536
2908
  }
@@ -2565,12 +2937,12 @@ function printListAsText(projectName, worktrees) {
2565
2937
  for (const wt of worktrees) {
2566
2938
  const status = getWorktreeStatus(wt);
2567
2939
  const isIdle = status ? isWorktreeIdle(status) : false;
2568
- const pathDisplay = isIdle ? chalk7.hex("#FF8C00")(wt.path) : wt.path;
2940
+ const pathDisplay = isIdle ? chalk8.hex("#FF8C00")(wt.path) : wt.path;
2569
2941
  printInfo(` ${pathDisplay} [${wt.branch}]`);
2570
2942
  if (status) {
2571
2943
  printInfo(` ${formatWorktreeStatus(status)}`);
2572
2944
  } else {
2573
- printInfo(` ${chalk7.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
2945
+ printInfo(` ${chalk8.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
2574
2946
  }
2575
2947
  printInfo("");
2576
2948
  }
@@ -2580,12 +2952,13 @@ function printListAsText(projectName, worktrees) {
2580
2952
 
2581
2953
  // src/commands/create.ts
2582
2954
  function registerCreateCommand(program2) {
2583
- 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) => {
2584
- handleCreate(options);
2955
+ 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) => {
2956
+ await handleCreate(options);
2585
2957
  });
2586
2958
  }
2587
- function handleCreate(options) {
2959
+ async function handleCreate(options) {
2588
2960
  validateMainWorktree();
2961
+ await ensureOnMainWorkBranch();
2589
2962
  const count = Number(options.number);
2590
2963
  if (!Number.isInteger(count) || count <= 0) {
2591
2964
  throw new ClawtError(
@@ -2601,6 +2974,7 @@ function handleCreate(options) {
2601
2974
  printInfo(`\u76EE\u5F55\u8DEF\u5F84${index + 1}\uFF1A`);
2602
2975
  printInfo(` ${wt.path}`);
2603
2976
  printInfo(` \u5206\u652F\u540D: ${wt.branch}`);
2977
+ printInfo(` \u9A8C\u8BC1\u5206\u652F: ${getValidateBranchName(wt.branch)}`);
2604
2978
  printSeparator();
2605
2979
  });
2606
2980
  }
@@ -2613,7 +2987,7 @@ var REMOVE_RESOLVE_MESSAGES = {
2613
2987
  noMatch: MESSAGES.REMOVE_NO_MATCH
2614
2988
  };
2615
2989
  function registerRemoveCommand(program2) {
2616
- 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) => {
2990
+ 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) => {
2617
2991
  await handleRemove(options);
2618
2992
  });
2619
2993
  }
@@ -2634,13 +3008,13 @@ async function handleRemove(options) {
2634
3008
  }
2635
3009
  printInfo("\u5373\u5C06\u79FB\u9664\u4EE5\u4E0B worktree \u53CA\u672C\u5730\u5206\u652F\uFF1A\n");
2636
3010
  worktreesToRemove.forEach((wt, index) => {
2637
- printInfo(` ${index + 1}. ${wt.path} \u2192 \u5206\u652F: ${wt.branch}`);
3011
+ printInfo(` ${index + 1}. ${wt.path} \u2192 \u5206\u652F: ${wt.branch} \u9A8C\u8BC1\u5206\u652F: ${getValidateBranchName(wt.branch)}`);
2638
3012
  });
2639
3013
  printInfo("");
2640
3014
  const autoDelete = getConfigValue("autoDeleteBranch");
2641
3015
  let shouldDeleteBranch = autoDelete;
2642
3016
  if (!autoDelete) {
2643
- shouldDeleteBranch = await confirmAction("\u662F\u5426\u540C\u65F6\u5220\u9664\u5BF9\u5E94\u7684\u672C\u5730\u5206\u652F\uFF1F");
3017
+ shouldDeleteBranch = await confirmAction(MESSAGES.REMOVE_CONFIRM_DELETE_BRANCHES);
2644
3018
  if (!shouldDeleteBranch) {
2645
3019
  printHint(MESSAGES.REMOVE_BRANCHES_KEPT);
2646
3020
  }
@@ -2648,10 +3022,12 @@ async function handleRemove(options) {
2648
3022
  const failures = [];
2649
3023
  for (const wt of worktreesToRemove) {
2650
3024
  try {
3025
+ await ensureOnMainWorkBranch();
2651
3026
  removeWorktreeByPath(wt.path);
2652
3027
  if (shouldDeleteBranch) {
2653
3028
  deleteBranch(wt.branch);
2654
3029
  }
3030
+ deleteValidateBranch(wt.branch);
2655
3031
  removeSnapshot(projectName, wt.branch);
2656
3032
  printSuccess(MESSAGES.WORKTREE_REMOVED(wt.path));
2657
3033
  } catch (error) {
@@ -2676,7 +3052,7 @@ async function handleRemove(options) {
2676
3052
 
2677
3053
  // src/commands/run.ts
2678
3054
  function registerRunCommand(program2) {
2679
- 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) => {
3055
+ 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) => {
2680
3056
  await handleRun(options);
2681
3057
  });
2682
3058
  }
@@ -2714,6 +3090,9 @@ function handleDryRunFromFile(options) {
2714
3090
  }
2715
3091
  async function handleRun(options) {
2716
3092
  validateMainWorktree();
3093
+ if (!options.dryRun) {
3094
+ await ensureOnMainWorkBranch();
3095
+ }
2717
3096
  if (options.file && options.tasks) {
2718
3097
  throw new ClawtError(MESSAGES.FILE_AND_TASKS_CONFLICT);
2719
3098
  }
@@ -2769,7 +3148,7 @@ var RESUME_RESOLVE_MESSAGES = {
2769
3148
  noMatch: MESSAGES.RESUME_NO_MATCH
2770
3149
  };
2771
3150
  function registerResumeCommand(program2) {
2772
- 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) => {
3151
+ 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) => {
2773
3152
  await handleResume(options);
2774
3153
  });
2775
3154
  }
@@ -2778,7 +3157,12 @@ async function handleResume(options) {
2778
3157
  validateClaudeCodeInstalled();
2779
3158
  logger.info(`resume \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F\u8FC7\u6EE4: ${options.branch ?? "(\u65E0)"}`);
2780
3159
  const worktrees = getProjectWorktrees();
2781
- const targetWorktrees = await resolveTargetWorktrees(worktrees, RESUME_RESOLVE_MESSAGES, options.branch);
3160
+ let targetWorktrees;
3161
+ if (!options.branch && worktrees.length > 1) {
3162
+ targetWorktrees = await promptGroupedMultiSelectBranches(worktrees, RESUME_RESOLVE_MESSAGES.selectBranch);
3163
+ } else {
3164
+ targetWorktrees = await resolveTargetWorktrees(worktrees, RESUME_RESOLVE_MESSAGES, options.branch);
3165
+ }
2782
3166
  if (targetWorktrees.length === 0) {
2783
3167
  return;
2784
3168
  }
@@ -2816,9 +3200,6 @@ async function handleBatchResume(worktrees) {
2816
3200
  printSuccess(MESSAGES.RESUME_ALL_SUCCESS(worktrees.length));
2817
3201
  }
2818
3202
 
2819
- // src/commands/validate.ts
2820
- import Enquirer4 from "enquirer";
2821
-
2822
3203
  // src/commands/sync.ts
2823
3204
  function registerSyncCommand(program2) {
2824
3205
  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) => {
@@ -2848,9 +3229,9 @@ function mergeMainBranch(worktreePath, mainBranch) {
2848
3229
  throw new ClawtError(`\u5408\u5E76 ${mainBranch} \u5931\u8D25`);
2849
3230
  }
2850
3231
  }
2851
- function executeSyncForBranch(targetWorktreePath, branch) {
3232
+ async function executeSyncForBranch(targetWorktreePath, branch) {
2852
3233
  const mainWorktreePath = getGitTopLevel();
2853
- const mainBranch = getCurrentBranch(mainWorktreePath);
3234
+ const mainBranch = getMainWorkBranch();
2854
3235
  if (!isWorkingDirClean(targetWorktreePath)) {
2855
3236
  autoSaveChanges(targetWorktreePath, branch);
2856
3237
  }
@@ -2866,15 +3247,20 @@ function executeSyncForBranch(targetWorktreePath, branch) {
2866
3247
  logger.info(`\u5DF2\u6E05\u9664\u5206\u652F ${branch} \u7684 validate \u5FEB\u7167`);
2867
3248
  }
2868
3249
  printSuccess(MESSAGES.SYNC_SUCCESS(branch, mainBranch));
3250
+ await rebuildValidateBranch(branch, mainWorktreePath);
3251
+ const validateBranchName = getValidateBranchName(branch);
3252
+ printInfo(MESSAGES.SYNC_VALIDATE_BRANCH_REBUILT(validateBranchName));
2869
3253
  return { success: true, hasConflict: false };
2870
3254
  }
2871
3255
  async function handleSync(options) {
2872
3256
  validateMainWorktree();
3257
+ requireProjectConfig();
3258
+ await ensureOnMainWorkBranch();
2873
3259
  logger.info(`sync \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch ?? "(\u672A\u6307\u5B9A)"}`);
2874
3260
  const worktrees = getProjectWorktrees();
2875
3261
  const worktree = await resolveTargetWorktree(worktrees, SYNC_RESOLVE_MESSAGES, options.branch);
2876
3262
  const { path: targetWorktreePath, branch } = worktree;
2877
- executeSyncForBranch(targetWorktreePath, branch);
3263
+ await executeSyncForBranch(targetWorktreePath, branch);
2878
3264
  }
2879
3265
 
2880
3266
  // src/commands/validate.ts
@@ -2885,43 +3271,12 @@ var VALIDATE_RESOLVE_MESSAGES = {
2885
3271
  noMatch: MESSAGES.VALIDATE_NO_MATCH
2886
3272
  };
2887
3273
  function registerValidateCommand(program2) {
2888
- 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) => {
3274
+ 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) => {
2889
3275
  await handleValidate(options);
2890
3276
  });
2891
3277
  }
2892
3278
  async function handleDirtyMainWorktree(mainWorktreePath) {
2893
- printWarning("\u4E3B worktree \u5F53\u524D\u5206\u652F\u6709\u672A\u63D0\u4EA4\u7684\u66F4\u6539\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F\uFF1A\n");
2894
- const choice = await new Enquirer4.Select({
2895
- message: "\u9009\u62E9\u5904\u7406\u65B9\u5F0F",
2896
- choices: [
2897
- {
2898
- name: "reset",
2899
- message: "reset (\u63A8\u8350) - \u4E22\u5F03\u6240\u6709\u66F4\u6539 (git reset --hard HEAD && git clean -fd)"
2900
- },
2901
- {
2902
- name: "stash",
2903
- message: "stash - \u6682\u5B58\u66F4\u6539 (git add . && git stash)"
2904
- },
2905
- {
2906
- name: "exit",
2907
- message: "exit - \u9000\u51FA\uFF0C\u624B\u52A8\u5904\u7406"
2908
- }
2909
- ],
2910
- initial: 0
2911
- }).run();
2912
- if (choice === "exit") {
2913
- throw new ClawtError("\u7528\u6237\u9009\u62E9\u9000\u51FA");
2914
- }
2915
- if (choice === "reset") {
2916
- gitResetHard(mainWorktreePath);
2917
- gitCleanForce(mainWorktreePath);
2918
- } else if (choice === "stash") {
2919
- gitAddAll(mainWorktreePath);
2920
- gitStashPush("clawt:auto-stash", mainWorktreePath);
2921
- }
2922
- if (!isWorkingDirClean(mainWorktreePath)) {
2923
- throw new ClawtError("\u5DE5\u4F5C\u533A\u4ECD\u7136\u4E0D\u5E72\u51C0\uFF0C\u8BF7\u624B\u52A8\u5904\u7406");
2924
- }
3279
+ await handleDirtyWorkingDir(mainWorktreePath);
2925
3280
  }
2926
3281
  function migrateChangesViaPatch(targetWorktreePath, mainWorktreePath, branchName, hasUncommitted) {
2927
3282
  let didTempCommit = false;
@@ -2964,7 +3319,7 @@ async function handlePatchApplyFailure(targetWorktreePath, branchName) {
2964
3319
  return;
2965
3320
  }
2966
3321
  printInfo(MESSAGES.VALIDATE_AUTO_SYNC_START(branchName));
2967
- const syncResult = executeSyncForBranch(targetWorktreePath, branchName);
3322
+ const syncResult = await executeSyncForBranch(targetWorktreePath, branchName);
2968
3323
  }
2969
3324
  function saveCurrentSnapshotTree(mainWorktreePath, projectName, branchName) {
2970
3325
  gitAddAll(mainWorktreePath);
@@ -2976,6 +3331,7 @@ function saveCurrentSnapshotTree(mainWorktreePath, projectName, branchName) {
2976
3331
  }
2977
3332
  async function handleValidateClean(options) {
2978
3333
  validateMainWorktree();
3334
+ requireProjectConfig();
2979
3335
  const projectName = getProjectName();
2980
3336
  const mainWorktreePath = getGitTopLevel();
2981
3337
  const worktrees = getProjectWorktrees();
@@ -2996,17 +3352,24 @@ async function handleValidateClean(options) {
2996
3352
  gitResetHard(mainWorktreePath);
2997
3353
  gitCleanForce(mainWorktreePath);
2998
3354
  }
3355
+ await ensureOnMainWorkBranch(mainWorktreePath);
2999
3356
  removeSnapshot(projectName, branchName);
3000
3357
  printSuccess(MESSAGES.VALIDATE_CLEANED(branchName));
3001
3358
  }
3002
3359
  async function handleFirstValidate(targetWorktreePath, mainWorktreePath, projectName, branchName, hasUncommitted) {
3360
+ const validateBranchName = getValidateBranchName(branchName);
3361
+ if (!checkBranchExists(validateBranchName)) {
3362
+ throw new ClawtError(MESSAGES.VALIDATE_BRANCH_NOT_FOUND(validateBranchName, branchName));
3363
+ }
3364
+ gitCheckout(validateBranchName, mainWorktreePath);
3003
3365
  const result = migrateChangesViaPatch(targetWorktreePath, mainWorktreePath, branchName, hasUncommitted);
3004
3366
  if (!result.success) {
3367
+ await ensureOnMainWorkBranch(mainWorktreePath);
3005
3368
  await handlePatchApplyFailure(targetWorktreePath, branchName);
3006
3369
  return;
3007
3370
  }
3008
3371
  saveCurrentSnapshotTree(mainWorktreePath, projectName, branchName);
3009
- printSuccess(MESSAGES.VALIDATE_SUCCESS(branchName));
3372
+ printSuccess(MESSAGES.VALIDATE_SUCCESS_WITH_BRANCH(branchName, validateBranchName));
3010
3373
  }
3011
3374
  async function handleIncrementalValidate(targetWorktreePath, mainWorktreePath, projectName, branchName, hasUncommitted) {
3012
3375
  const { treeHash: oldTreeHash, headCommitHash: oldHeadCommitHash } = readSnapshot(projectName, branchName);
@@ -3014,8 +3377,17 @@ async function handleIncrementalValidate(targetWorktreePath, mainWorktreePath, p
3014
3377
  gitResetHard(mainWorktreePath);
3015
3378
  gitCleanForce(mainWorktreePath);
3016
3379
  }
3380
+ const validateBranchName = getValidateBranchName(branchName);
3381
+ if (!checkBranchExists(validateBranchName)) {
3382
+ throw new ClawtError(MESSAGES.VALIDATE_BRANCH_NOT_FOUND(validateBranchName, branchName));
3383
+ }
3384
+ const currentBranch = getCurrentBranch(mainWorktreePath);
3385
+ if (currentBranch !== validateBranchName) {
3386
+ gitCheckout(validateBranchName, mainWorktreePath);
3387
+ }
3017
3388
  const result = migrateChangesViaPatch(targetWorktreePath, mainWorktreePath, branchName, hasUncommitted);
3018
3389
  if (!result.success) {
3390
+ await ensureOnMainWorkBranch(mainWorktreePath);
3019
3391
  await handlePatchApplyFailure(targetWorktreePath, branchName);
3020
3392
  return;
3021
3393
  }
@@ -3030,7 +3402,7 @@ async function handleIncrementalValidate(targetWorktreePath, mainWorktreePath, p
3030
3402
  } else if (oldChangePatch.length > 0) {
3031
3403
  logger.warn("\u65E7\u53D8\u66F4 patch \u4E0E\u5F53\u524D HEAD \u51B2\u7A81\uFF0C\u964D\u7EA7\u4E3A\u5168\u91CF\u6A21\u5F0F");
3032
3404
  printWarning(MESSAGES.INCREMENTAL_VALIDATE_FALLBACK);
3033
- printSuccess(MESSAGES.VALIDATE_SUCCESS(branchName));
3405
+ printSuccess(MESSAGES.VALIDATE_SUCCESS_WITH_BRANCH(branchName, validateBranchName));
3034
3406
  return;
3035
3407
  }
3036
3408
  } else {
@@ -3039,7 +3411,7 @@ async function handleIncrementalValidate(targetWorktreePath, mainWorktreePath, p
3039
3411
  } catch (error) {
3040
3412
  logger.warn(`\u589E\u91CF read-tree \u5931\u8D25: ${error}`);
3041
3413
  printWarning(MESSAGES.INCREMENTAL_VALIDATE_FALLBACK);
3042
- printSuccess(MESSAGES.VALIDATE_SUCCESS(branchName));
3414
+ printSuccess(MESSAGES.VALIDATE_SUCCESS_WITH_BRANCH(branchName, validateBranchName));
3043
3415
  return;
3044
3416
  }
3045
3417
  printSuccess(MESSAGES.INCREMENTAL_VALIDATE_SUCCESS(branchName));
@@ -3103,6 +3475,7 @@ async function handleValidate(options) {
3103
3475
  return;
3104
3476
  }
3105
3477
  validateMainWorktree();
3478
+ requireProjectConfig();
3106
3479
  const projectName = getProjectName();
3107
3480
  const mainWorktreePath = getGitTopLevel();
3108
3481
  const worktrees = getProjectWorktrees();
@@ -3135,7 +3508,7 @@ async function handleValidate(options) {
3135
3508
 
3136
3509
  // src/commands/merge.ts
3137
3510
  function registerMergeCommand(program2) {
3138
- 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) => {
3511
+ 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) => {
3139
3512
  await handleMerge(options);
3140
3513
  });
3141
3514
  }
@@ -3153,7 +3526,7 @@ async function handleSquashIfNeeded(targetWorktreePath, mainWorktreePath, branch
3153
3526
  if (!shouldSquash) {
3154
3527
  return false;
3155
3528
  }
3156
- const mainBranch = getCurrentBranch(mainWorktreePath);
3529
+ const mainBranch = getMainWorkBranch();
3157
3530
  const mergeBase = gitMergeBase(mainBranch, branchName, mainWorktreePath);
3158
3531
  logger.info(`squash: merge-base = ${mergeBase}, \u5206\u652F = ${branchName}`);
3159
3532
  gitResetSoftTo(mergeBase, targetWorktreePath);
@@ -3180,6 +3553,7 @@ function cleanupWorktreeAndBranch(worktreePath, branchName) {
3180
3553
  async function handleMerge(options) {
3181
3554
  validateMainWorktree();
3182
3555
  const mainWorktreePath = getGitTopLevel();
3556
+ await ensureOnMainWorkBranch(mainWorktreePath);
3183
3557
  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)"}`);
3184
3558
  const worktrees = getProjectWorktrees();
3185
3559
  const worktree = await resolveTargetWorktree(worktrees, MERGE_RESOLVE_MESSAGES, options.branch);
@@ -3253,7 +3627,7 @@ async function handleMerge(options) {
3253
3627
  }
3254
3628
 
3255
3629
  // src/commands/config.ts
3256
- import chalk8 from "chalk";
3630
+ import chalk9 from "chalk";
3257
3631
  import Enquirer5 from "enquirer";
3258
3632
  function registerConfigCommand(program2) {
3259
3633
  const configCmd = program2.command("config").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u5168\u5C40\u914D\u7F6E").action(async () => {
@@ -3314,7 +3688,7 @@ async function handleInteractiveConfigSet() {
3314
3688
  const isObject = typeof DEFAULT_CONFIG[k] === "object";
3315
3689
  return {
3316
3690
  name: k,
3317
- message: `${k}: ${isObject ? chalk8.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk8.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
3691
+ message: `${k}: ${isObject ? chalk9.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk9.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
3318
3692
  ...isObject && { disabled: CONFIG_ALIAS_DISABLED_HINT }
3319
3693
  };
3320
3694
  });
@@ -3347,6 +3721,7 @@ function registerResetCommand(program2) {
3347
3721
  }
3348
3722
  async function handleReset() {
3349
3723
  validateMainWorktree();
3724
+ requireProjectConfig();
3350
3725
  const mainWorktreePath = getGitTopLevel();
3351
3726
  logger.info("reset \u547D\u4EE4\u6267\u884C");
3352
3727
  if (!isWorkingDirClean(mainWorktreePath)) {
@@ -3369,9 +3744,9 @@ async function handleReset() {
3369
3744
  }
3370
3745
 
3371
3746
  // src/commands/status.ts
3372
- import chalk9 from "chalk";
3747
+ import chalk10 from "chalk";
3373
3748
  function registerStatusCommand(program2) {
3374
- program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
3749
+ 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").action((options) => {
3375
3750
  handleStatus(options);
3376
3751
  });
3377
3752
  }
@@ -3482,7 +3857,7 @@ function printStatusAsJson(result) {
3482
3857
  }
3483
3858
  function printStatusAsText(result) {
3484
3859
  printDoubleSeparator();
3485
- printInfo(` ${chalk9.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
3860
+ printInfo(` ${chalk10.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
3486
3861
  printDoubleSeparator();
3487
3862
  printInfo("");
3488
3863
  printMainSection(result.main);
@@ -3495,17 +3870,17 @@ function printStatusAsText(result) {
3495
3870
  printDoubleSeparator();
3496
3871
  }
3497
3872
  function printMainSection(main2) {
3498
- printInfo(` ${chalk9.bold("\u25C6")} ${chalk9.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
3499
- printInfo(` \u5206\u652F: ${chalk9.bold(main2.branch)}`);
3873
+ printInfo(` ${chalk10.bold("\u25C6")} ${chalk10.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
3874
+ printInfo(` \u5206\u652F: ${chalk10.bold(main2.branch)}`);
3500
3875
  if (main2.isClean) {
3501
- printInfo(` \u72B6\u6001: ${chalk9.green("\u2713 \u5E72\u51C0")}`);
3876
+ printInfo(` \u72B6\u6001: ${chalk10.green("\u2713 \u5E72\u51C0")}`);
3502
3877
  } else {
3503
- printInfo(` \u72B6\u6001: ${chalk9.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
3878
+ printInfo(` \u72B6\u6001: ${chalk10.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
3504
3879
  }
3505
3880
  printInfo("");
3506
3881
  }
3507
3882
  function printWorktreesSection(worktrees, total) {
3508
- printInfo(` ${chalk9.bold("\u25C6")} ${chalk9.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
3883
+ printInfo(` ${chalk10.bold("\u25C6")} ${chalk10.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
3509
3884
  printInfo("");
3510
3885
  if (worktrees.length === 0) {
3511
3886
  printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
@@ -3517,56 +3892,56 @@ function printWorktreesSection(worktrees, total) {
3517
3892
  }
3518
3893
  function printWorktreeItem(wt) {
3519
3894
  const statusLabel = formatChangeStatusLabel(wt.changeStatus);
3520
- printInfo(` ${chalk9.bold("\u25CF")} ${chalk9.bold(wt.branch)} [${statusLabel}]`);
3895
+ printInfo(` ${chalk10.bold("\u25CF")} ${chalk10.bold(wt.branch)} [${statusLabel}]`);
3521
3896
  if (wt.insertions > 0 || wt.deletions > 0) {
3522
- printInfo(` ${chalk9.green(`+${wt.insertions}`)} ${chalk9.red(`-${wt.deletions}`)}`);
3897
+ printInfo(` ${chalk10.green(`+${wt.insertions}`)} ${chalk10.red(`-${wt.deletions}`)}`);
3523
3898
  }
3524
3899
  if (wt.commitsAhead > 0) {
3525
- printInfo(` ${chalk9.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
3900
+ printInfo(` ${chalk10.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
3526
3901
  }
3527
3902
  if (wt.commitsBehind > 0) {
3528
- printInfo(` ${chalk9.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
3903
+ printInfo(` ${chalk10.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
3529
3904
  } else {
3530
- printInfo(` ${chalk9.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
3905
+ printInfo(` ${chalk10.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
3531
3906
  }
3532
3907
  if (wt.createdAt) {
3533
3908
  const relativeTime = formatRelativeTime(wt.createdAt);
3534
3909
  if (relativeTime) {
3535
- printInfo(` ${chalk9.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
3910
+ printInfo(` ${chalk10.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
3536
3911
  }
3537
3912
  }
3538
3913
  if (wt.snapshotTime) {
3539
3914
  const relativeTime = formatRelativeTime(wt.snapshotTime);
3540
3915
  if (relativeTime) {
3541
- printInfo(` ${chalk9.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
3916
+ printInfo(` ${chalk10.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
3542
3917
  }
3543
3918
  } else {
3544
- printInfo(` ${chalk9.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
3919
+ printInfo(` ${chalk10.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
3545
3920
  }
3546
3921
  printInfo("");
3547
3922
  }
3548
3923
  function formatChangeStatusLabel(status) {
3549
3924
  switch (status) {
3550
3925
  case "committed":
3551
- return chalk9.green(MESSAGES.STATUS_CHANGE_COMMITTED);
3926
+ return chalk10.green(MESSAGES.STATUS_CHANGE_COMMITTED);
3552
3927
  case "uncommitted":
3553
- return chalk9.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
3928
+ return chalk10.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
3554
3929
  case "conflict":
3555
- return chalk9.red(MESSAGES.STATUS_CHANGE_CONFLICT);
3930
+ return chalk10.red(MESSAGES.STATUS_CHANGE_CONFLICT);
3556
3931
  case "clean":
3557
- return chalk9.gray(MESSAGES.STATUS_CHANGE_CLEAN);
3932
+ return chalk10.gray(MESSAGES.STATUS_CHANGE_CLEAN);
3558
3933
  }
3559
3934
  }
3560
3935
  function printSnapshotsSection(snapshots) {
3561
- printInfo(` ${chalk9.bold("\u25C6")} ${chalk9.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
3936
+ printInfo(` ${chalk10.bold("\u25C6")} ${chalk10.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
3562
3937
  if (snapshots.orphaned > 0) {
3563
- printInfo(` ${chalk9.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
3938
+ printInfo(` ${chalk10.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
3564
3939
  }
3565
3940
  printInfo("");
3566
3941
  }
3567
3942
 
3568
3943
  // src/commands/alias.ts
3569
- import chalk10 from "chalk";
3944
+ import chalk11 from "chalk";
3570
3945
  function getRegisteredCommandNames(program2) {
3571
3946
  return program2.commands.map((cmd) => cmd.name());
3572
3947
  }
@@ -3587,7 +3962,7 @@ ${MESSAGES.ALIAS_LIST_TITLE}
3587
3962
  `);
3588
3963
  printSeparator();
3589
3964
  for (const [alias, command] of entries) {
3590
- printInfo(` ${chalk10.bold(alias)} \u2192 ${chalk10.cyan(command)}`);
3965
+ printInfo(` ${chalk11.bold(alias)} \u2192 ${chalk11.cyan(command)}`);
3591
3966
  }
3592
3967
  printInfo("");
3593
3968
  printSeparator();
@@ -3619,7 +3994,7 @@ function handleAliasRemove(alias) {
3619
3994
  printSuccess(MESSAGES.ALIAS_REMOVE_SUCCESS(alias));
3620
3995
  }
3621
3996
  function registerAliasCommand(program2) {
3622
- const aliasCmd = program2.command("alias").description("\u7BA1\u7406\u547D\u4EE4\u522B\u540D").action(() => {
3997
+ const aliasCmd = program2.command("alias").description("\u7BA1\u7406\u547D\u4EE4\u522B\u540D\uFF08\u5217\u51FA / \u8BBE\u7F6E / \u79FB\u9664\uFF09").action(() => {
3623
3998
  handleAliasList();
3624
3999
  });
3625
4000
  aliasCmd.command("list").description("\u5217\u51FA\u6240\u6709\u522B\u540D").action(() => {
@@ -3634,9 +4009,9 @@ function registerAliasCommand(program2) {
3634
4009
  }
3635
4010
 
3636
4011
  // src/commands/projects.ts
3637
- import { existsSync as existsSync9, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
3638
- import { join as join7 } from "path";
3639
- import chalk11 from "chalk";
4012
+ import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
4013
+ import { join as join8 } from "path";
4014
+ import chalk12 from "chalk";
3640
4015
  function registerProjectsCommand(program2) {
3641
4016
  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) => {
3642
4017
  handleProjects({ name, json: options.json });
@@ -3659,8 +4034,8 @@ function handleProjectsOverview(json) {
3659
4034
  printProjectsOverviewAsText(result);
3660
4035
  }
3661
4036
  function handleProjectDetail(name, json) {
3662
- const projectDir = join7(WORKTREES_DIR, name);
3663
- if (!existsSync9(projectDir)) {
4037
+ const projectDir = join8(WORKTREES_DIR, name);
4038
+ if (!existsSync10(projectDir)) {
3664
4039
  printError(MESSAGES.PROJECTS_NOT_FOUND(name));
3665
4040
  process.exit(1);
3666
4041
  }
@@ -3673,7 +4048,7 @@ function handleProjectDetail(name, json) {
3673
4048
  printProjectDetailAsText(result);
3674
4049
  }
3675
4050
  function collectProjectsOverview() {
3676
- if (!existsSync9(WORKTREES_DIR)) {
4051
+ if (!existsSync10(WORKTREES_DIR)) {
3677
4052
  return { projects: [], totalProjects: 0, totalDiskUsage: 0 };
3678
4053
  }
3679
4054
  const entries = readdirSync5(WORKTREES_DIR, { withFileTypes: true });
@@ -3682,7 +4057,7 @@ function collectProjectsOverview() {
3682
4057
  if (!entry.isDirectory()) {
3683
4058
  continue;
3684
4059
  }
3685
- const projectDir = join7(WORKTREES_DIR, entry.name);
4060
+ const projectDir = join8(WORKTREES_DIR, entry.name);
3686
4061
  const overview = collectSingleProjectOverview(entry.name, projectDir);
3687
4062
  projects.push(overview);
3688
4063
  }
@@ -3699,7 +4074,7 @@ function collectSingleProjectOverview(name, projectDir) {
3699
4074
  const worktreeDirs = subEntries.filter((e) => e.isDirectory());
3700
4075
  const worktreeCount = worktreeDirs.length;
3701
4076
  const diskUsage = calculateDirSize(projectDir);
3702
- const lastActiveTime = resolveProjectLastActiveTime(projectDir, worktreeDirs.map((e) => join7(projectDir, e.name)));
4077
+ const lastActiveTime = resolveProjectLastActiveTime(projectDir, worktreeDirs.map((e) => join8(projectDir, e.name)));
3703
4078
  return {
3704
4079
  name,
3705
4080
  worktreeCount,
@@ -3714,7 +4089,7 @@ function collectProjectDetail(name, projectDir) {
3714
4089
  if (!entry.isDirectory()) {
3715
4090
  continue;
3716
4091
  }
3717
- const wtPath = join7(projectDir, entry.name);
4092
+ const wtPath = join8(projectDir, entry.name);
3718
4093
  const detail = collectSingleWorktreeDetail(entry.name, wtPath);
3719
4094
  worktrees.push(detail);
3720
4095
  }
@@ -3728,7 +4103,7 @@ function collectProjectDetail(name, projectDir) {
3728
4103
  };
3729
4104
  }
3730
4105
  function collectSingleWorktreeDetail(branch, wtPath) {
3731
- const stat = statSync3(wtPath);
4106
+ const stat = statSync4(wtPath);
3732
4107
  const diskUsage = calculateDirSize(wtPath);
3733
4108
  return {
3734
4109
  branch,
@@ -3738,10 +4113,10 @@ function collectSingleWorktreeDetail(branch, wtPath) {
3738
4113
  };
3739
4114
  }
3740
4115
  function resolveProjectLastActiveTime(projectDir, worktreePaths) {
3741
- let latestTime = statSync3(projectDir).mtime;
4116
+ let latestTime = statSync4(projectDir).mtime;
3742
4117
  for (const wtPath of worktreePaths) {
3743
4118
  try {
3744
- const wtStat = statSync3(wtPath);
4119
+ const wtStat = statSync4(wtPath);
3745
4120
  if (wtStat.mtime > latestTime) {
3746
4121
  latestTime = wtStat.mtime;
3747
4122
  }
@@ -3755,7 +4130,7 @@ function sortByLastActiveTimeDesc(projects) {
3755
4130
  }
3756
4131
  function printProjectsOverviewAsText(result) {
3757
4132
  printDoubleSeparator();
3758
- printInfo(` ${chalk11.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
4133
+ printInfo(` ${chalk12.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
3759
4134
  printDoubleSeparator();
3760
4135
  printInfo("");
3761
4136
  if (result.projects.length === 0) {
@@ -3769,7 +4144,7 @@ function printProjectsOverviewAsText(result) {
3769
4144
  }
3770
4145
  printSeparator();
3771
4146
  printInfo("");
3772
- printInfo(` \u5171 ${chalk11.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk11.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
4147
+ printInfo(` \u5171 ${chalk12.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk12.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
3773
4148
  printInfo("");
3774
4149
  printDoubleSeparator();
3775
4150
  }
@@ -3777,16 +4152,16 @@ function printProjectOverviewItem(project) {
3777
4152
  const relativeTime = formatRelativeTime(project.lastActiveTime);
3778
4153
  const activeLabel = relativeTime ? MESSAGES.PROJECTS_LAST_ACTIVE(relativeTime) : "";
3779
4154
  const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(project.diskUsage));
3780
- printInfo(` ${chalk11.bold("\u25CF")} ${chalk11.bold(project.name)}`);
3781
- printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk11.gray(activeLabel)} ${chalk11.gray(diskLabel)}`);
4155
+ printInfo(` ${chalk12.bold("\u25CF")} ${chalk12.bold(project.name)}`);
4156
+ printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk12.gray(activeLabel)} ${chalk12.gray(diskLabel)}`);
3782
4157
  printInfo("");
3783
4158
  }
3784
4159
  function printProjectDetailAsText(result) {
3785
4160
  printDoubleSeparator();
3786
- printInfo(` ${chalk11.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
4161
+ printInfo(` ${chalk12.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
3787
4162
  printDoubleSeparator();
3788
4163
  printInfo("");
3789
- printInfo(` ${chalk11.bold("\u25C6")} ${chalk11.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
4164
+ printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
3790
4165
  printInfo(` ${MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage))}`);
3791
4166
  printInfo("");
3792
4167
  printSeparator();
@@ -3806,14 +4181,14 @@ function printWorktreeDetailItem(wt) {
3806
4181
  const relativeTime = formatRelativeTime(wt.lastModifiedTime);
3807
4182
  const modifiedLabel = relativeTime ? MESSAGES.PROJECTS_LAST_MODIFIED(relativeTime) : "";
3808
4183
  const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(wt.diskUsage));
3809
- printInfo(` ${chalk11.bold("\u25CF")} ${chalk11.bold(wt.branch)}`);
4184
+ printInfo(` ${chalk12.bold("\u25CF")} ${chalk12.bold(wt.branch)}`);
3810
4185
  printInfo(` ${wt.path}`);
3811
- printInfo(` ${chalk11.gray(modifiedLabel)} ${chalk11.gray(diskLabel)}`);
4186
+ printInfo(` ${chalk12.gray(modifiedLabel)} ${chalk12.gray(diskLabel)}`);
3812
4187
  printInfo("");
3813
4188
  }
3814
4189
 
3815
4190
  // src/commands/completion.ts
3816
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync11 } from "fs";
4191
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync12 } from "fs";
3817
4192
  import { resolve as resolve2 } from "path";
3818
4193
  import { homedir as homedir2 } from "os";
3819
4194
 
@@ -3864,14 +4239,14 @@ compdef _clawt_completion clawt
3864
4239
  }
3865
4240
 
3866
4241
  // src/utils/completion-engine.ts
3867
- import { existsSync as existsSync10, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
3868
- import { join as join8, dirname, basename as basename2 } from "path";
4242
+ import { existsSync as existsSync11, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
4243
+ import { join as join9, dirname, basename as basename2 } from "path";
3869
4244
  function completeFilePath(partial) {
3870
4245
  const cwd = process.cwd();
3871
4246
  const hasDir = partial.includes("/");
3872
- const searchDir = hasDir ? join8(cwd, dirname(partial)) : cwd;
4247
+ const searchDir = hasDir ? join9(cwd, dirname(partial)) : cwd;
3873
4248
  const prefix = hasDir ? basename2(partial) : partial;
3874
- if (!existsSync10(searchDir)) {
4249
+ if (!existsSync11(searchDir)) {
3875
4250
  return [];
3876
4251
  }
3877
4252
  const entries = readdirSync6(searchDir);
@@ -3880,9 +4255,9 @@ function completeFilePath(partial) {
3880
4255
  for (const entry of entries) {
3881
4256
  if (!entry.startsWith(prefix)) continue;
3882
4257
  if (entry.startsWith(".")) continue;
3883
- const fullPath = join8(searchDir, entry);
4258
+ const fullPath = join9(searchDir, entry);
3884
4259
  try {
3885
- const stat = statSync4(fullPath);
4260
+ const stat = statSync5(fullPath);
3886
4261
  if (stat.isDirectory()) {
3887
4262
  results.push(dirPrefix + entry + "/");
3888
4263
  } else if (stat.isFile()) {
@@ -3969,15 +4344,15 @@ function generateCompletions(program2, args) {
3969
4344
 
3970
4345
  // src/commands/completion.ts
3971
4346
  function appendToFile(filePath, content) {
3972
- if (existsSync11(filePath)) {
3973
- const current = readFileSync5(filePath, "utf-8");
4347
+ if (existsSync12(filePath)) {
4348
+ const current = readFileSync6(filePath, "utf-8");
3974
4349
  if (current.includes("clawt completion")) {
3975
4350
  printInfo(MESSAGES.COMPLETION_INSTALL_EXISTS + ": " + filePath);
3976
4351
  return;
3977
4352
  }
3978
4353
  content = current + content;
3979
4354
  }
3980
- writeFileSync4(filePath, content, "utf-8");
4355
+ writeFileSync5(filePath, content, "utf-8");
3981
4356
  printSuccess(MESSAGES.COMPLETION_INSTALL_SUCCESS + ": " + filePath);
3982
4357
  printInfo(MESSAGES.COMPLETION_INSTALL_RESTART(filePath));
3983
4358
  }
@@ -4024,6 +4399,37 @@ function registerCompletionCommand(program2) {
4024
4399
  });
4025
4400
  }
4026
4401
 
4402
+ // src/commands/init.ts
4403
+ import { Command as Cmd } from "commander";
4404
+ function registerInitCommand(program2) {
4405
+ 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) => {
4406
+ await handleInit(options);
4407
+ });
4408
+ initCmd.addCommand(
4409
+ new Cmd("show").description("\u5C55\u793A\u5F53\u524D\u9879\u76EE\u7684 init \u914D\u7F6E").action(() => {
4410
+ handleInitShow();
4411
+ })
4412
+ );
4413
+ }
4414
+ function handleInitShow() {
4415
+ validateMainWorktree();
4416
+ const config2 = requireProjectConfig();
4417
+ const configJson = safeStringify(config2);
4418
+ printInfo(MESSAGES.INIT_SHOW(configJson));
4419
+ }
4420
+ async function handleInit(options) {
4421
+ validateMainWorktree();
4422
+ const existingConfig = loadProjectConfig();
4423
+ const branchName = options.branch || getCurrentBranch();
4424
+ logger.info(`init \u547D\u4EE4\u6267\u884C\uFF0C\u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}`);
4425
+ saveProjectConfig({ clawtMainWorkBranch: branchName });
4426
+ if (existingConfig) {
4427
+ printSuccess(MESSAGES.INIT_UPDATED(existingConfig.clawtMainWorkBranch, branchName));
4428
+ } else {
4429
+ printSuccess(MESSAGES.INIT_SUCCESS(branchName));
4430
+ }
4431
+ }
4432
+
4027
4433
  // src/index.ts
4028
4434
  var require2 = createRequire(import.meta.url);
4029
4435
  var { version } = require2("../package.json");
@@ -4049,6 +4455,7 @@ registerStatusCommand(program);
4049
4455
  registerAliasCommand(program);
4050
4456
  registerProjectsCommand(program);
4051
4457
  registerCompletionCommand(program);
4458
+ registerInitCommand(program);
4052
4459
  var config = loadConfig();
4053
4460
  applyAliases(program, config.aliases);
4054
4461
  process.on("uncaughtException", (error) => {