clawt 2.10.0 → 2.11.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 (57) hide show
  1. package/.claude/agent-memory/docs-sync-updater/MEMORY.md +14 -7
  2. package/.claude/agents/docs-sync-updater.md +11 -0
  3. package/README.md +80 -284
  4. package/dist/index.js +839 -307
  5. package/dist/postinstall.js +272 -0
  6. package/docs/spec.md +84 -22
  7. package/package.json +1 -1
  8. package/src/commands/remove.ts +21 -28
  9. package/src/commands/run.ts +68 -206
  10. package/src/constants/config.ts +4 -0
  11. package/src/constants/index.ts +11 -1
  12. package/src/constants/messages/common.ts +41 -0
  13. package/src/constants/messages/config.ts +5 -0
  14. package/src/constants/messages/create.ts +5 -0
  15. package/src/constants/messages/index.ts +29 -0
  16. package/src/constants/messages/merge.ts +42 -0
  17. package/src/constants/messages/remove.ts +15 -0
  18. package/src/constants/messages/reset.ts +7 -0
  19. package/src/constants/messages/resume.ts +12 -0
  20. package/src/constants/messages/run.ts +46 -0
  21. package/src/constants/messages/status.ts +25 -0
  22. package/src/constants/messages/sync.ts +24 -0
  23. package/src/constants/messages/validate.ts +25 -0
  24. package/src/constants/progress.ts +39 -0
  25. package/src/types/command.ts +4 -0
  26. package/src/types/config.ts +2 -0
  27. package/src/types/index.ts +1 -0
  28. package/src/types/taskFile.ts +13 -0
  29. package/src/utils/formatter.ts +16 -0
  30. package/src/utils/index.ts +8 -4
  31. package/src/utils/progress-render.ts +90 -0
  32. package/src/utils/progress.ts +213 -0
  33. package/src/utils/task-executor.ts +365 -0
  34. package/src/utils/task-file.ts +87 -0
  35. package/src/utils/worktree-matcher.ts +92 -0
  36. package/src/utils/worktree.ts +27 -0
  37. package/tests/unit/commands/config.test.ts +110 -0
  38. package/tests/unit/commands/create.test.ts +115 -0
  39. package/tests/unit/commands/list.test.ts +118 -0
  40. package/tests/unit/commands/merge.test.ts +323 -0
  41. package/tests/unit/commands/remove.test.ts +240 -0
  42. package/tests/unit/commands/reset.test.ts +124 -0
  43. package/tests/unit/commands/resume.test.ts +91 -0
  44. package/tests/unit/commands/run.test.ts +456 -0
  45. package/tests/unit/commands/status.test.ts +214 -0
  46. package/tests/unit/commands/sync.test.ts +208 -0
  47. package/tests/unit/commands/validate.test.ts +382 -0
  48. package/tests/unit/constants/config.test.ts +1 -0
  49. package/tests/unit/constants/messages.test.ts +1 -1
  50. package/tests/unit/utils/config.test.ts +21 -1
  51. package/tests/unit/utils/formatter.test.ts +70 -1
  52. package/tests/unit/utils/git.test.ts +44 -0
  53. package/tests/unit/utils/progress.test.ts +255 -0
  54. package/tests/unit/utils/task-file.test.ts +236 -0
  55. package/tests/unit/utils/validate-snapshot.test.ts +25 -0
  56. package/tests/unit/utils/worktree-matcher.test.ts +81 -5
  57. package/tests/unit/utils/worktree.test.ts +26 -1
package/dist/index.js CHANGED
@@ -18,8 +18,8 @@ var VALIDATE_SNAPSHOTS_DIR = join(CLAWT_HOME, "validate-snapshots");
18
18
  // src/constants/branch.ts
19
19
  var INVALID_BRANCH_CHARS = /[\/\\.\s~:*?[\]^]+/g;
20
20
 
21
- // src/constants/messages.ts
22
- var MESSAGES = {
21
+ // src/constants/messages/common.ts
22
+ var COMMON_MESSAGES = {
23
23
  /** 不在主 worktree 根目录 */
24
24
  NOT_MAIN_WORKTREE: "\u8BF7\u5728\u4E3B worktree \u7684\u6839\u76EE\u5F55\u4E0B\u6267\u884C clawt",
25
25
  /** Git 未安装 */
@@ -28,8 +28,6 @@ var MESSAGES = {
28
28
  CLAUDE_NOT_INSTALLED: "Claude Code CLI \u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u5B89\u88C5\uFF1Anpm install -g @anthropic-ai/claude-code",
29
29
  /** 分支已存在 */
30
30
  BRANCH_EXISTS: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u65E0\u6CD5\u521B\u5EFA`,
31
- /** 分支已存在时提示使用 resume */
32
- BRANCH_EXISTS_USE_RESUME: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u4F7F\u7528 clawt resume -b ${name} \u6062\u590D\u4F1A\u8BDD`,
33
31
  /** 分支名清理后为空 */
34
32
  BRANCH_NAME_EMPTY: (original) => `\u5206\u652F\u540D "${original}" \u4E2D\u4E0D\u5305\u542B\u5408\u6CD5\u5B57\u7B26\uFF0C\u65E0\u6CD5\u521B\u5EFA\u5206\u652F`,
35
33
  /** 分支名被转换 */
@@ -46,24 +44,24 @@ var MESSAGES = {
46
44
  MAIN_WORKTREE_DIRTY: "\u4E3B worktree \u6709\u672A\u63D0\u4EA4\u7684\u66F4\u6539\uFF0C\u8BF7\u5148\u5904\u7406",
47
45
  /** 目标 worktree 无更改 */
48
46
  TARGET_WORKTREE_CLEAN: "\u8BE5 worktree \u7684\u5206\u652F\u4E0A\u6CA1\u6709\u4EFB\u4F55\u66F4\u6539\uFF0C\u65E0\u9700\u9A8C\u8BC1",
49
- /** validate 成功 */
50
- VALIDATE_SUCCESS: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u53D8\u66F4\u5E94\u7528\u5230\u4E3B worktree
51
- \u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86`,
52
- /** merge 成功 */
53
- MERGE_SUCCESS: (branch, message, pushed) => `\u2713 \u5206\u652F ${branch} \u5DF2\u6210\u529F\u5408\u5E76\u5230\u5F53\u524D\u5206\u652F
54
- \u63D0\u4EA4\u4FE1\u606F: ${message}${pushed ? "\n \u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\u4ED3\u5E93" : ""}`,
55
- /** merge 成功(无提交信息,目标 worktree 已提交过) */
56
- MERGE_SUCCESS_NO_MESSAGE: (branch, pushed) => `\u2713 \u5206\u652F ${branch} \u5DF2\u6210\u529F\u5408\u5E76\u5230\u5F53\u524D\u5206\u652F${pushed ? "\n \u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\u4ED3\u5E93" : ""}`,
57
- /** merge 冲突 */
58
- MERGE_CONFLICT: "\u5408\u5E76\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u624B\u52A8\u5904\u7406\uFF1A\n \u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue",
59
- /** merge 后清理 worktree 和分支成功 */
60
- WORKTREE_CLEANED: (branch) => `\u2713 \u5DF2\u6E05\u7406 worktree \u548C\u5206\u652F: ${branch}`,
47
+ /** 用户取消破坏性操作 */
48
+ DESTRUCTIVE_OP_CANCELLED: "\u5DF2\u53D6\u6D88\u64CD\u4F5C",
61
49
  /** 请提供提交信息 */
62
50
  COMMIT_MESSAGE_REQUIRED: "\u8BF7\u63D0\u4F9B\u63D0\u4EA4\u4FE1\u606F\uFF08-m \u53C2\u6570\uFF09",
63
- /** 目标 worktree 有未提交修改但未指定 -m */
64
- TARGET_WORKTREE_DIRTY_NO_MESSAGE: "\u76EE\u6807 worktree \u6709\u672A\u63D0\u4EA4\u7684\u4FEE\u6539\uFF0C\u8BF7\u901A\u8FC7 -m \u53C2\u6570\u63D0\u4F9B\u63D0\u4EA4\u4FE1\u606F",
65
- /** 目标 worktree 既干净又无本地提交 */
66
- TARGET_WORKTREE_NO_CHANGES: "\u76EE\u6807 worktree \u6CA1\u6709\u4EFB\u4F55\u53EF\u5408\u5E76\u7684\u53D8\u66F4\uFF08\u5DE5\u4F5C\u533A\u5E72\u51C0\u4E14\u65E0\u672C\u5730\u63D0\u4EA4\uFF09",
51
+ /** 配置文件损坏,已重新生成默认配置 */
52
+ CONFIG_CORRUPTED: "\u914D\u7F6E\u6587\u4EF6\u635F\u574F\u6216\u65E0\u6CD5\u89E3\u6790\uFF0C\u5DF2\u91CD\u65B0\u751F\u6210\u9ED8\u8BA4\u914D\u7F6E",
53
+ /** worktree 状态获取失败 */
54
+ WORKTREE_STATUS_UNAVAILABLE: "(\u72B6\u6001\u4E0D\u53EF\u7528)",
55
+ /** 分隔线 */
56
+ SEPARATOR: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
57
+ /** 粗分隔线 */
58
+ DOUBLE_SEPARATOR: "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
59
+ };
60
+
61
+ // src/constants/messages/run.ts
62
+ var RUN_MESSAGES = {
63
+ /** 分支已存在时提示使用 resume */
64
+ BRANCH_EXISTS_USE_RESUME: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u4F7F\u7528 clawt resume -b ${name} \u6062\u590D\u4F1A\u8BDD`,
67
65
  /** 检测到用户中断 */
68
66
  INTERRUPTED: "\u68C0\u6D4B\u5230\u9000\u51FA\u6307\u4EE4\uFF0C\u5DF2\u505C\u6B62 Claude Code \u4EFB\u52A1",
69
67
  /** 中断后自动清理完成 */
@@ -74,41 +72,57 @@ var MESSAGES = {
74
72
  INTERRUPT_CLEANED: (count) => `\u2713 \u5DF2\u6E05\u7406 ${count} \u4E2A worktree \u548C\u5BF9\u5E94\u5206\u652F`,
75
73
  /** 中断后保留 worktree */
76
74
  INTERRUPT_KEPT: "\u5DF2\u4FDD\u7559 worktree\uFF0C\u53EF\u7A0D\u540E\u4F7F\u7528 clawt remove \u624B\u52A8\u6E05\u7406",
77
- /** 配置文件损坏,已重新生成默认配置 */
78
- CONFIG_CORRUPTED: "\u914D\u7F6E\u6587\u4EF6\u635F\u574F\u6216\u65E0\u6CD5\u89E3\u6790\uFF0C\u5DF2\u91CD\u65B0\u751F\u6210\u9ED8\u8BA4\u914D\u7F6E",
79
- /** 配置已恢复为默认值 */
80
- CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C",
81
- /** 分隔线 */
82
- SEPARATOR: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
83
- /** 粗分隔线 */
84
- DOUBLE_SEPARATOR: "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
75
+ /** TTY 环境降级输出:任务启动 */
76
+ PROGRESS_TASK_STARTED: (index, total, branch, path) => `[${index}/${total}] ${branch} \u542F\u52A8 ${path}`,
77
+ /** TTY 环境降级输出:任务完成 */
78
+ PROGRESS_TASK_DONE: (index, total, branch, duration, cost, path) => `[${index}/${total}] ${branch} \u2713 \u5B8C\u6210 ${duration} ${cost} ${path}`,
79
+ /** TTY 环境降级输出:任务失败 */
80
+ PROGRESS_TASK_FAILED: (index, total, branch, duration, path) => `[${index}/${total}] ${branch} \u2717 \u5931\u8D25 ${duration} ${path}`,
81
+ /** 并发限制提示 */
82
+ CONCURRENCY_INFO: (concurrency, total) => `\u5E76\u53D1\u9650\u5236: ${concurrency}\uFF0C\u5171 ${total} \u4E2A\u4EFB\u52A1`,
83
+ /** 并发数无效提示 */
84
+ CONCURRENCY_INVALID: "\u5E76\u53D1\u6570\u5FC5\u987B\u4E3A\u6B63\u6574\u6570",
85
+ /** 任务文件不存在 */
86
+ TASK_FILE_NOT_FOUND: (path) => `\u4EFB\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728: ${path}`,
87
+ /** 任务文件中没有解析到有效任务 */
88
+ TASK_FILE_EMPTY: "\u4EFB\u52A1\u6587\u4EF6\u4E2D\u6CA1\u6709\u89E3\u6790\u5230\u6709\u6548\u4EFB\u52A1",
89
+ /** 任务文件中某个任务块缺少分支名 */
90
+ TASK_FILE_MISSING_BRANCH: (blockIndex) => `\u4EFB\u52A1\u6587\u4EF6\u7B2C ${blockIndex} \u4E2A\u4EFB\u52A1\u5757\u7F3A\u5C11\u5206\u652F\u540D\uFF08# branch: ...\uFF09`,
91
+ /** 任务文件中某个任务块缺少任务描述 */
92
+ TASK_FILE_MISSING_TASK: (branch) => `\u4EFB\u52A1\u6587\u4EF6\u4E2D\u5206\u652F ${branch} \u7F3A\u5C11\u4EFB\u52A1\u63CF\u8FF0`,
93
+ /** 任务文件中某个任务块缺少任务描述(无分支名时按索引定位) */
94
+ TASK_FILE_MISSING_TASK_BY_INDEX: (blockIndex) => `\u4EFB\u52A1\u6587\u4EF6\u7B2C ${blockIndex} \u4E2A\u4EFB\u52A1\u5757\u7F3A\u5C11\u4EFB\u52A1\u63CF\u8FF0`,
95
+ /** --file 和 --tasks 不能同时使用 */
96
+ FILE_AND_TASKS_CONFLICT: "--file \u548C --tasks \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528",
97
+ /** 任务文件加载成功 */
98
+ TASK_FILE_LOADED: (count, path) => `\u2713 \u4ECE ${path} \u52A0\u8F7D\u4E86 ${count} \u4E2A\u4EFB\u52A1`,
99
+ /** 未指定 -b 或 -f */
100
+ BRANCH_OR_FILE_REQUIRED: "\u8BF7\u6307\u5B9A -b \u5206\u652F\u540D\u6216 -f \u4EFB\u52A1\u6587\u4EF6"
101
+ };
102
+
103
+ // src/constants/messages/create.ts
104
+ var CREATE_MESSAGES = {
85
105
  /** 创建数量参数无效 */
86
- INVALID_COUNT: (value) => `\u65E0\u6548\u7684\u521B\u5EFA\u6570\u91CF: "${value}"\uFF0C\u8BF7\u8F93\u5165\u6B63\u6574\u6570`,
87
- /** worktree 状态获取失败 */
88
- WORKTREE_STATUS_UNAVAILABLE: "(\u72B6\u6001\u4E0D\u53EF\u7528)",
89
- /** 增量 validate 成功提示 */
90
- INCREMENTAL_VALIDATE_SUCCESS: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u6700\u65B0\u53D8\u66F4\u5E94\u7528\u5230\u4E3B worktree\uFF08\u589E\u91CF\u6A21\u5F0F\uFF09
91
- \u6682\u5B58\u533A = \u4E0A\u6B21\u5FEB\u7167\uFF0C\u5DE5\u4F5C\u76EE\u5F55 = \u6700\u65B0\u53D8\u66F4`,
92
- /** 增量 validate 降级为全量模式提示 */
93
- INCREMENTAL_VALIDATE_FALLBACK: "\u589E\u91CF\u5BF9\u6BD4\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u5168\u91CF\u6A21\u5F0F",
94
- /** validate 状态已清理 */
95
- VALIDATE_CLEANED: (branch) => `\u2713 \u5206\u652F ${branch} \u7684 validate \u72B6\u6001\u5DF2\u6E05\u7406`,
106
+ INVALID_COUNT: (value) => `\u65E0\u6548\u7684\u521B\u5EFA\u6570\u91CF: "${value}"\uFF0C\u8BF7\u8F93\u5165\u6B63\u6574\u6570`
107
+ };
108
+
109
+ // src/constants/messages/merge.ts
110
+ var MERGE_MESSAGES = {
111
+ /** merge 成功 */
112
+ MERGE_SUCCESS: (branch, message, pushed) => `\u2713 \u5206\u652F ${branch} \u5DF2\u6210\u529F\u5408\u5E76\u5230\u5F53\u524D\u5206\u652F
113
+ \u63D0\u4EA4\u4FE1\u606F: ${message}${pushed ? "\n \u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\u4ED3\u5E93" : ""}`,
114
+ /** merge 成功(无提交信息,目标 worktree 已提交过) */
115
+ MERGE_SUCCESS_NO_MESSAGE: (branch, pushed) => `\u2713 \u5206\u652F ${branch} \u5DF2\u6210\u529F\u5408\u5E76\u5230\u5F53\u524D\u5206\u652F${pushed ? "\n \u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\u4ED3\u5E93" : ""}`,
116
+ /** merge 冲突 */
117
+ MERGE_CONFLICT: "\u5408\u5E76\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u624B\u52A8\u5904\u7406\uFF1A\n \u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue",
118
+ /** merge 后清理 worktree 和分支成功 */
119
+ WORKTREE_CLEANED: (branch) => `\u2713 \u5DF2\u6E05\u7406 worktree \u548C\u5206\u652F: ${branch}`,
120
+ /** 目标 worktree 有未提交修改但未指定 -m */
121
+ TARGET_WORKTREE_DIRTY_NO_MESSAGE: "\u76EE\u6807 worktree \u6709\u672A\u63D0\u4EA4\u7684\u4FEE\u6539\uFF0C\u8BF7\u901A\u8FC7 -m \u53C2\u6570\u63D0\u4F9B\u63D0\u4EA4\u4FE1\u606F",
122
+ /** 目标 worktree 既干净又无本地提交 */
123
+ TARGET_WORKTREE_NO_CHANGES: "\u76EE\u6807 worktree \u6CA1\u6709\u4EFB\u4F55\u53EF\u5408\u5E76\u7684\u53D8\u66F4\uFF08\u5DE5\u4F5C\u533A\u5E72\u51C0\u4E14\u65E0\u672C\u5730\u63D0\u4EA4\uFF09",
96
124
  /** merge 命令检测到 validate 状态的提示 */
97
125
  MERGE_VALIDATE_STATE_HINT: (branch) => `\u4E3B worktree \u53EF\u80FD\u5B58\u5728 validate \u6B8B\u7559\u72B6\u6001\uFF0C\u53EF\u5148\u6267\u884C clawt validate -b ${branch} --clean \u6E05\u7406`,
98
- /** sync 自动保存未提交变更 */
99
- SYNC_AUTO_COMMITTED: (branch) => `\u5DF2\u81EA\u52A8\u4FDD\u5B58 ${branch} \u5206\u652F\u7684\u672A\u63D0\u4EA4\u53D8\u66F4`,
100
- /** sync 开始合并 */
101
- SYNC_MERGING: (targetBranch, mainBranch) => `\u6B63\u5728\u5C06 ${mainBranch} \u5408\u5E76\u5230 ${targetBranch} ...`,
102
- /** sync 成功 */
103
- SYNC_SUCCESS: (targetBranch, mainBranch) => `\u2713 \u5DF2\u5C06 ${mainBranch} \u7684\u6700\u65B0\u4EE3\u7801\u540C\u6B65\u5230 ${targetBranch}`,
104
- /** sync 冲突 */
105
- SYNC_CONFLICT: (worktreePath) => `\u5408\u5E76\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u8FDB\u5165\u76EE\u6807 worktree \u624B\u52A8\u89E3\u51B3\uFF1A
106
- cd ${worktreePath}
107
- \u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue
108
- clawt validate -b <branch> \u9A8C\u8BC1\u53D8\u66F4`,
109
- /** validate patch apply 失败,提示用户同步主分支 */
110
- VALIDATE_PATCH_APPLY_FAILED: (branch) => `\u53D8\u66F4\u8FC1\u79FB\u5931\u8D25\uFF1A\u76EE\u6807\u5206\u652F\u4E0E\u4E3B\u5206\u652F\u5DEE\u5F02\u8FC7\u5927
111
- \u8BF7\u5148\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`,
112
126
  /** merge 检测到 auto-save 提交,提示用户是否压缩 */
113
127
  MERGE_SQUASH_PROMPT: "\u68C0\u6D4B\u5230 sync \u4EA7\u751F\u7684\u4E34\u65F6\u63D0\u4EA4\uFF0C\u662F\u5426\u5C06\u6240\u6709\u63D0\u4EA4\u538B\u7F29\u4E3A\u4E00\u4E2A\uFF1F\n \u538B\u7F29\u540E\u53D8\u66F4\u5C06\u4FDD\u7559\u5728\u76EE\u6807worktree\u7684\u6682\u5B58\u533A\uFF0C\u9700\u8981\u91CD\u65B0\u63D0\u4EA4\uFF08\u53EF\u4F7F\u7528 Claude Code Cli\u6216\u5176\u4ED6\u5DE5\u5177\u751F\u6210\u63D0\u4EA4\u4FE1\u606F\uFF09",
114
128
  /** squash 完成且通过 -m 直接提交后的提示 */
@@ -118,25 +132,37 @@ var MESSAGES = {
118
132
  \u8BF7\u5728\u76EE\u6807 worktree \u4E2D\u63D0\u4EA4\u540E\u91CD\u65B0\u6267\u884C merge\uFF1A
119
133
  cd ${worktreePath}
120
134
  \u63D0\u4EA4\u5B8C\u6210\u540E\u6267\u884C\uFF1Aclawt merge -b ${branch}`,
121
- /** 用户取消破坏性操作 */
122
- DESTRUCTIVE_OP_CANCELLED: "\u5DF2\u53D6\u6D88\u64CD\u4F5C",
123
- /** reset 成功 */
124
- RESET_SUCCESS: "\u2713 \u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u91CD\u7F6E",
125
- /** reset 时工作区和暂存区已干净 */
126
- RESET_ALREADY_CLEAN: "\u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u662F\u5E72\u51C0\u72B6\u6001\uFF0C\u65E0\u9700\u91CD\u7F6E",
127
- /** 批量移除部分失败 */
128
- REMOVE_PARTIAL_FAILURE: (failures) => `\u4EE5\u4E0B worktree \u79FB\u9664\u5931\u8D25\uFF1A
129
- ${failures.map((f) => ` \u2717 ${f.path}: ${f.error}`).join("\n")}`,
130
- /** resume 无可用 worktree */
131
- RESUME_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
132
- /** resume 模糊匹配无结果,列出可用分支 */
133
- RESUME_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
135
+ /** merge 后 pull 冲突 */
136
+ PULL_CONFLICT: "\u81EA\u52A8 pull \u65F6\u53D1\u751F\u51B2\u7A81\uFF0Cmerge \u5DF2\u5B8C\u6210\u4F46\u8FDC\u7A0B\u540C\u6B65\u5931\u8D25\n \u8BF7\u624B\u52A8\u89E3\u51B3\u51B2\u7A81\uFF1A\n \u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git commit\n \u7136\u540E\u6267\u884C git push \u63A8\u9001\u5230\u8FDC\u7A0B",
137
+ /** push 失败 */
138
+ PUSH_FAILED: "\u81EA\u52A8 push \u5931\u8D25\uFF0Cmerge \u548C pull \u5DF2\u5B8C\u6210\n \u8BF7\u624B\u52A8\u6267\u884C git push",
139
+ /** merge 无可用 worktree */
140
+ MERGE_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
141
+ /** merge 模糊匹配无结果,列出可用分支 */
142
+ MERGE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
134
143
  \u53EF\u7528\u5206\u652F\uFF1A
135
144
  ${branches.map((b) => ` - ${b}`).join("\n")}`,
136
- /** resume 交互选择提示 */
137
- RESUME_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u6062\u590D\u7684\u5206\u652F",
138
- /** resume 模糊匹配到多个结果提示 */
139
- RESUME_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`,
145
+ /** merge 交互选择提示 */
146
+ MERGE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u5408\u5E76\u7684\u5206\u652F",
147
+ /** merge 模糊匹配到多个结果提示 */
148
+ MERGE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
149
+ };
150
+
151
+ // src/constants/messages/validate.ts
152
+ var VALIDATE_MESSAGES = {
153
+ /** validate 成功 */
154
+ VALIDATE_SUCCESS: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u53D8\u66F4\u5E94\u7528\u5230\u4E3B worktree
155
+ \u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86`,
156
+ /** 增量 validate 成功提示 */
157
+ INCREMENTAL_VALIDATE_SUCCESS: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u6700\u65B0\u53D8\u66F4\u5E94\u7528\u5230\u4E3B worktree\uFF08\u589E\u91CF\u6A21\u5F0F\uFF09
158
+ \u6682\u5B58\u533A = \u4E0A\u6B21\u5FEB\u7167\uFF0C\u5DE5\u4F5C\u76EE\u5F55 = \u6700\u65B0\u53D8\u66F4`,
159
+ /** 增量 validate 降级为全量模式提示 */
160
+ INCREMENTAL_VALIDATE_FALLBACK: "\u589E\u91CF\u5BF9\u6BD4\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u5168\u91CF\u6A21\u5F0F",
161
+ /** validate 状态已清理 */
162
+ VALIDATE_CLEANED: (branch) => `\u2713 \u5206\u652F ${branch} \u7684 validate \u72B6\u6001\u5DF2\u6E05\u7406`,
163
+ /** validate patch apply 失败,提示用户同步主分支 */
164
+ VALIDATE_PATCH_APPLY_FAILED: (branch) => `\u53D8\u66F4\u8FC1\u79FB\u5931\u8D25\uFF1A\u76EE\u6807\u5206\u652F\u4E0E\u4E3B\u5206\u652F\u5DEE\u5F02\u8FC7\u5927
165
+ \u8BF7\u5148\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`,
140
166
  /** validate 无可用 worktree */
141
167
  VALIDATE_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
142
168
  /** validate 模糊匹配无结果,列出可用分支 */
@@ -146,17 +172,22 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
146
172
  /** validate 交互选择提示 */
147
173
  VALIDATE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u9A8C\u8BC1\u7684\u5206\u652F",
148
174
  /** validate 模糊匹配到多个结果提示 */
149
- VALIDATE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`,
150
- /** merge 无可用 worktree */
151
- MERGE_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
152
- /** merge 模糊匹配无结果,列出可用分支 */
153
- MERGE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
154
- \u53EF\u7528\u5206\u652F\uFF1A
155
- ${branches.map((b) => ` - ${b}`).join("\n")}`,
156
- /** merge 交互选择提示 */
157
- MERGE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u5408\u5E76\u7684\u5206\u652F",
158
- /** merge 模糊匹配到多个结果提示 */
159
- MERGE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`,
175
+ VALIDATE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
176
+ };
177
+
178
+ // src/constants/messages/sync.ts
179
+ var SYNC_MESSAGES = {
180
+ /** sync 自动保存未提交变更 */
181
+ SYNC_AUTO_COMMITTED: (branch) => `\u5DF2\u81EA\u52A8\u4FDD\u5B58 ${branch} \u5206\u652F\u7684\u672A\u63D0\u4EA4\u53D8\u66F4`,
182
+ /** sync 开始合并 */
183
+ SYNC_MERGING: (targetBranch, mainBranch) => `\u6B63\u5728\u5C06 ${mainBranch} \u5408\u5E76\u5230 ${targetBranch} ...`,
184
+ /** sync 成功 */
185
+ SYNC_SUCCESS: (targetBranch, mainBranch) => `\u2713 \u5DF2\u5C06 ${mainBranch} \u7684\u6700\u65B0\u4EE3\u7801\u540C\u6B65\u5230 ${targetBranch}`,
186
+ /** sync 冲突 */
187
+ SYNC_CONFLICT: (worktreePath) => `\u5408\u5E76\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u8FDB\u5165\u76EE\u6807 worktree \u624B\u52A8\u89E3\u51B3\uFF1A
188
+ cd ${worktreePath}
189
+ \u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue
190
+ clawt validate -b <branch> \u9A8C\u8BC1\u53D8\u66F4`,
160
191
  /** sync 无可用 worktree */
161
192
  SYNC_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
162
193
  /** sync 模糊匹配无结果,列出可用分支 */
@@ -166,7 +197,56 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
166
197
  /** sync 交互选择提示 */
167
198
  SYNC_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u540C\u6B65\u7684\u5206\u652F",
168
199
  /** sync 模糊匹配到多个结果提示 */
169
- SYNC_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`,
200
+ SYNC_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
201
+ };
202
+
203
+ // src/constants/messages/resume.ts
204
+ var RESUME_MESSAGES = {
205
+ /** resume 无可用 worktree */
206
+ RESUME_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
207
+ /** resume 模糊匹配无结果,列出可用分支 */
208
+ RESUME_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
209
+ \u53EF\u7528\u5206\u652F\uFF1A
210
+ ${branches.map((b) => ` - ${b}`).join("\n")}`,
211
+ /** resume 交互选择提示 */
212
+ RESUME_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u6062\u590D\u7684\u5206\u652F",
213
+ /** resume 模糊匹配到多个结果提示 */
214
+ RESUME_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
215
+ };
216
+
217
+ // src/constants/messages/remove.ts
218
+ var REMOVE_MESSAGES = {
219
+ /** remove 无可用 worktree */
220
+ REMOVE_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u65E0\u9700\u79FB\u9664",
221
+ /** remove 多选交互提示 */
222
+ REMOVE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u79FB\u9664\u7684\u5206\u652F\uFF08\u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
223
+ /** remove 模糊匹配到多个结果提示 */
224
+ REMOVE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\u8981\u79FB\u9664\u7684\uFF08\u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09\uFF1A`,
225
+ /** remove 模糊匹配无结果,列出可用分支 */
226
+ REMOVE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
227
+ \u53EF\u7528\u5206\u652F\uFF1A
228
+ ${branches.map((b) => ` - ${b}`).join("\n")}`,
229
+ /** 批量移除部分失败 */
230
+ REMOVE_PARTIAL_FAILURE: (failures) => `\u4EE5\u4E0B worktree \u79FB\u9664\u5931\u8D25\uFF1A
231
+ ${failures.map((f) => ` \u2717 ${f.path}: ${f.error}`).join("\n")}`
232
+ };
233
+
234
+ // src/constants/messages/reset.ts
235
+ var RESET_MESSAGES = {
236
+ /** reset 成功 */
237
+ RESET_SUCCESS: "\u2713 \u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u91CD\u7F6E",
238
+ /** reset 时工作区和暂存区已干净 */
239
+ RESET_ALREADY_CLEAN: "\u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u662F\u5E72\u51C0\u72B6\u6001\uFF0C\u65E0\u9700\u91CD\u7F6E"
240
+ };
241
+
242
+ // src/constants/messages/config.ts
243
+ var CONFIG_CMD_MESSAGES = {
244
+ /** 配置已恢复为默认值 */
245
+ CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C"
246
+ };
247
+
248
+ // src/constants/messages/status.ts
249
+ var STATUS_MESSAGES = {
170
250
  /** status 命令标题 */
171
251
  STATUS_TITLE: (projectName) => `\u9879\u76EE\u72B6\u6001\u603B\u89C8: ${projectName}`,
172
252
  /** status 主 worktree 区块标题 */
@@ -188,11 +268,22 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
188
268
  /** status 变更状态:无变更 */
189
269
  STATUS_CHANGE_CLEAN: "\u65E0\u53D8\u66F4",
190
270
  /** status 快照对应 worktree 已不存在 */
191
- STATUS_SNAPSHOT_ORPHANED: "(\u5BF9\u5E94 worktree \u5DF2\u4E0D\u5B58\u5728)",
192
- /** merge 后 pull 冲突 */
193
- PULL_CONFLICT: "\u81EA\u52A8 pull \u65F6\u53D1\u751F\u51B2\u7A81\uFF0Cmerge \u5DF2\u5B8C\u6210\u4F46\u8FDC\u7A0B\u540C\u6B65\u5931\u8D25\n \u8BF7\u624B\u52A8\u89E3\u51B3\u51B2\u7A81\uFF1A\n \u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git commit\n \u7136\u540E\u6267\u884C git push \u63A8\u9001\u5230\u8FDC\u7A0B",
194
- /** push 失败 */
195
- PUSH_FAILED: "\u81EA\u52A8 push \u5931\u8D25\uFF0Cmerge \u548C pull \u5DF2\u5B8C\u6210\n \u8BF7\u624B\u52A8\u6267\u884C git push"
271
+ STATUS_SNAPSHOT_ORPHANED: "(\u5BF9\u5E94 worktree \u5DF2\u4E0D\u5B58\u5728)"
272
+ };
273
+
274
+ // src/constants/messages/index.ts
275
+ var MESSAGES = {
276
+ ...COMMON_MESSAGES,
277
+ ...RUN_MESSAGES,
278
+ ...CREATE_MESSAGES,
279
+ ...MERGE_MESSAGES,
280
+ ...VALIDATE_MESSAGES,
281
+ ...SYNC_MESSAGES,
282
+ ...RESUME_MESSAGES,
283
+ ...REMOVE_MESSAGES,
284
+ ...RESET_MESSAGES,
285
+ ...CONFIG_CMD_MESSAGES,
286
+ ...STATUS_MESSAGES
196
287
  };
197
288
 
198
289
  // src/constants/exitCodes.ts
@@ -223,6 +314,10 @@ var CONFIG_DEFINITIONS = {
223
314
  confirmDestructiveOps: {
224
315
  defaultValue: true,
225
316
  description: "\u6267\u884C\u7834\u574F\u6027\u64CD\u4F5C\uFF08reset\u3001validate --clean\uFF09\u524D\u662F\u5426\u63D0\u793A\u786E\u8BA4"
317
+ },
318
+ maxConcurrency: {
319
+ defaultValue: 0,
320
+ description: "run \u547D\u4EE4\u9ED8\u8BA4\u6700\u5927\u5E76\u53D1\u6570\uFF0C0 \u8868\u793A\u4E0D\u9650\u5236"
226
321
  }
227
322
  };
228
323
  function deriveDefaultConfig(definitions) {
@@ -246,6 +341,32 @@ var AUTO_SAVE_COMMIT_MESSAGE = "chore: auto-save before sync";
246
341
  // src/constants/logger.ts
247
342
  var DEBUG_TIMESTAMP_FORMAT = "HH:mm:ss.SSS";
248
343
 
344
+ // src/constants/progress.ts
345
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
346
+ var SPINNER_INTERVAL_MS = 100;
347
+ var CURSOR_UP = (n) => `\x1B[${n}A`;
348
+ var CLEAR_LINE = "\x1B[0K";
349
+ var CURSOR_HIDE = "\x1B[?25l";
350
+ var CURSOR_SHOW = "\x1B[?25h";
351
+ var TASK_STATUS_ICONS = {
352
+ /** 排队中 */
353
+ PENDING: "\u25E6",
354
+ /** 完成 */
355
+ DONE: "\u2713",
356
+ /** 失败 */
357
+ FAILED: "\u2717"
358
+ };
359
+ var TASK_STATUS_LABELS = {
360
+ /** 排队中 */
361
+ PENDING: "\u6392\u961F\u4E2D",
362
+ /** 运行中 */
363
+ RUNNING: "\u8FD0\u884C\u4E2D",
364
+ /** 完成 */
365
+ DONE: "\u5B8C\u6210",
366
+ /** 失败 */
367
+ FAILED: "\u5931\u8D25"
368
+ };
369
+
249
370
  // src/errors/index.ts
250
371
  var ClawtError = class extends Error {
251
372
  /** 退出码 */
@@ -551,14 +672,14 @@ function printDoubleSeparator() {
551
672
  console.log(MESSAGES.DOUBLE_SEPARATOR);
552
673
  }
553
674
  function confirmAction(question) {
554
- return new Promise((resolve) => {
675
+ return new Promise((resolve2) => {
555
676
  const rl = createInterface({
556
677
  input: process.stdin,
557
678
  output: process.stdout
558
679
  });
559
680
  rl.question(`${question} (y/N) `, (answer) => {
560
681
  rl.close();
561
- resolve(answer.toLowerCase() === "y");
682
+ resolve2(answer.toLowerCase() === "y");
562
683
  });
563
684
  });
564
685
  }
@@ -585,6 +706,15 @@ function formatWorktreeStatus(status) {
585
706
  }
586
707
  return parts.join(" ");
587
708
  }
709
+ function formatDuration(ms) {
710
+ const totalSeconds = ms / 1e3;
711
+ if (totalSeconds < 60) {
712
+ return `${totalSeconds.toFixed(1)}s`;
713
+ }
714
+ const minutes = Math.floor(totalSeconds / 60);
715
+ const seconds = Math.floor(totalSeconds % 60);
716
+ return `${minutes}m${String(seconds).padStart(2, "0")}s`;
717
+ }
588
718
 
589
719
  // src/utils/branch.ts
590
720
  function sanitizeBranchName(branchName) {
@@ -677,6 +807,19 @@ function createWorktrees(branchName, count) {
677
807
  }
678
808
  return results;
679
809
  }
810
+ function createWorktreesByBranches(branchNames) {
811
+ validateBranchesNotExist(branchNames);
812
+ const projectDir = getProjectWorktreeDir();
813
+ ensureDir(projectDir);
814
+ const results = [];
815
+ for (const name of branchNames) {
816
+ const worktreePath = join2(projectDir, name);
817
+ createWorktree(name, worktreePath);
818
+ results.push({ path: worktreePath, branch: name });
819
+ logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
820
+ }
821
+ return results;
822
+ }
680
823
  function getProjectWorktrees() {
681
824
  const projectDir = getProjectWorktreeDir();
682
825
  if (!existsSync3(projectDir)) {
@@ -871,6 +1014,44 @@ async function promptSelectBranch(worktrees, message) {
871
1014
  }).run();
872
1015
  return worktrees.find((wt) => wt.branch === selectedBranch);
873
1016
  }
1017
+ async function promptMultiSelectBranches(worktrees, message) {
1018
+ const selectedBranches = await new Enquirer2.MultiSelect({
1019
+ message,
1020
+ choices: worktrees.map((wt) => ({
1021
+ name: wt.branch,
1022
+ message: wt.branch
1023
+ })),
1024
+ // 使用空心圆/实心圆作为选中指示符
1025
+ symbols: {
1026
+ indicator: { on: "\u25CF", off: "\u25CB" }
1027
+ }
1028
+ }).run();
1029
+ return worktrees.filter((wt) => selectedBranches.includes(wt.branch));
1030
+ }
1031
+ async function resolveTargetWorktrees(worktrees, messages, branchName) {
1032
+ if (worktrees.length === 0) {
1033
+ throw new ClawtError(messages.noWorktrees);
1034
+ }
1035
+ if (!branchName) {
1036
+ if (worktrees.length === 1) {
1037
+ return [worktrees[0]];
1038
+ }
1039
+ return promptMultiSelectBranches(worktrees, messages.selectBranch);
1040
+ }
1041
+ const exactMatch = findExactMatch(worktrees, branchName);
1042
+ if (exactMatch) {
1043
+ return [exactMatch];
1044
+ }
1045
+ const fuzzyMatches = findFuzzyMatches(worktrees, branchName);
1046
+ if (fuzzyMatches.length === 1) {
1047
+ return [fuzzyMatches[0]];
1048
+ }
1049
+ if (fuzzyMatches.length > 1) {
1050
+ return promptMultiSelectBranches(fuzzyMatches, messages.multipleMatches(branchName));
1051
+ }
1052
+ const allBranches = worktrees.map((wt) => wt.branch);
1053
+ throw new ClawtError(messages.noMatch(branchName, allBranches));
1054
+ }
874
1055
  async function resolveTargetWorktree(worktrees, messages, branchName) {
875
1056
  if (worktrees.length === 0) {
876
1057
  throw new ClawtError(messages.noWorktrees);
@@ -896,160 +1077,262 @@ async function resolveTargetWorktree(worktrees, messages, branchName) {
896
1077
  throw new ClawtError(messages.noMatch(branchName, allBranches));
897
1078
  }
898
1079
 
899
- // src/commands/list.ts
1080
+ // src/utils/progress-render.ts
900
1081
  import chalk3 from "chalk";
901
- function registerListCommand(program2) {
902
- program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
903
- handleList(options);
904
- });
905
- }
906
- function handleList(options) {
907
- validateMainWorktree();
908
- const projectName = getProjectName();
909
- const worktrees = getProjectWorktrees();
910
- logger.info(`list \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}\uFF0C\u5171 ${worktrees.length} \u4E2A worktree`);
911
- if (options.json) {
912
- printListAsJson(projectName, worktrees);
913
- return;
1082
+ function getMaxBranchWidth(tasks) {
1083
+ return Math.max(...tasks.map((t) => t.branch.length));
1084
+ }
1085
+ function renderTaskLine(task, total, maxBranchWidth, spinnerChar) {
1086
+ const indexStr = `[${task.index}/${total}]`;
1087
+ const branchStr = task.branch.padEnd(maxBranchWidth);
1088
+ switch (task.status) {
1089
+ case "pending": {
1090
+ return `${indexStr} ${branchStr} ${chalk3.gray(TASK_STATUS_ICONS.PENDING)} ${chalk3.gray(TASK_STATUS_LABELS.PENDING)} ${chalk3.dim(task.path)}`;
1091
+ }
1092
+ case "running": {
1093
+ const elapsed = formatDuration(Date.now() - task.startedAt);
1094
+ return `${indexStr} ${branchStr} ${chalk3.cyan(spinnerChar)} ${chalk3.cyan(TASK_STATUS_LABELS.RUNNING)} ${chalk3.gray(elapsed)} ${chalk3.dim(task.path)}`;
1095
+ }
1096
+ case "done": {
1097
+ const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
1098
+ const cost = task.costUsd != null ? `$${task.costUsd.toFixed(2)}` : "";
1099
+ return `${indexStr} ${branchStr} ${chalk3.green(TASK_STATUS_ICONS.DONE)} ${chalk3.green(TASK_STATUS_LABELS.DONE)} ${chalk3.gray(duration)} ${chalk3.yellow(cost)} ${chalk3.dim(task.path)}`;
1100
+ }
1101
+ case "failed": {
1102
+ const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
1103
+ return `${indexStr} ${branchStr} ${chalk3.red(TASK_STATUS_ICONS.FAILED)} ${chalk3.red(TASK_STATUS_LABELS.FAILED)} ${chalk3.gray(duration)} ${chalk3.dim(task.path)}`;
1104
+ }
914
1105
  }
915
- printListAsText(projectName, worktrees);
916
1106
  }
917
- function printListAsJson(projectName, worktrees) {
918
- const result = {
919
- project: projectName,
920
- total: worktrees.length,
921
- worktrees: worktrees.map((wt) => ({
922
- path: wt.path,
923
- branch: wt.branch
924
- }))
925
- };
926
- console.log(JSON.stringify(result, null, 2));
1107
+ function renderSummaryLine(tasks, total) {
1108
+ const running = tasks.filter((t) => t.status === "running").length;
1109
+ const done = tasks.filter((t) => t.status === "done").length;
1110
+ const failed = tasks.filter((t) => t.status === "failed").length;
1111
+ const pending = tasks.filter((t) => t.status === "pending").length;
1112
+ const parts = [];
1113
+ if (running > 0) parts.push(chalk3.cyan(`${running}/${total} ${TASK_STATUS_LABELS.RUNNING}`));
1114
+ if (done > 0) parts.push(chalk3.green(`${done}/${total} ${TASK_STATUS_LABELS.DONE}`));
1115
+ if (failed > 0) parts.push(chalk3.red(`${failed}/${total} ${TASK_STATUS_LABELS.FAILED}`));
1116
+ if (pending > 0) parts.push(chalk3.gray(`${pending}/${total} ${TASK_STATUS_LABELS.PENDING}`));
1117
+ return `[${parts.join(", ")}]`;
927
1118
  }
928
- function printListAsText(projectName, worktrees) {
929
- printInfo(`\u5F53\u524D\u9879\u76EE: ${projectName}
930
- `);
931
- if (worktrees.length === 0) {
932
- printInfo(` ${MESSAGES.NO_WORKTREES}`);
933
- } else {
934
- for (const wt of worktrees) {
935
- const status = getWorktreeStatus(wt);
936
- const isIdle = status ? isWorktreeIdle(status) : false;
937
- const pathDisplay = isIdle ? chalk3.hex("#FF8C00")(wt.path) : wt.path;
938
- printInfo(` ${pathDisplay} [${wt.branch}]`);
939
- if (status) {
940
- printInfo(` ${formatWorktreeStatus(status)}`);
941
- } else {
942
- printInfo(` ${chalk3.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
1119
+
1120
+ // src/utils/progress.ts
1121
+ var ProgressRenderer = class {
1122
+ /** 所有任务的进度状态 */
1123
+ tasks;
1124
+ /** 总任务数 */
1125
+ total;
1126
+ /** 当前 spinner 帧索引 */
1127
+ frameIndex;
1128
+ /** 定时器引用 */
1129
+ timer;
1130
+ /** 是否为 TTY 环境 */
1131
+ isTTY;
1132
+ /** 已渲染的行数(用于回退光标) */
1133
+ renderedLineCount;
1134
+ /** 是否已停止 */
1135
+ stopped;
1136
+ /** 是否存在排队任务(启用汇总行渲染) */
1137
+ hasPendingTasks;
1138
+ /**
1139
+ * 创建进度面板渲染器
1140
+ * @param {string[]} branches - 分支名列表,顺序对应任务列表
1141
+ * @param {string[]} paths - worktree 路径列表,完成/失败后显示
1142
+ * @param {boolean} [allRunning=true] - 是否将所有任务初始化为 running 状态,false 时初始化为 pending
1143
+ */
1144
+ constructor(branches, paths, allRunning = true) {
1145
+ const now = Date.now();
1146
+ this.total = branches.length;
1147
+ this.frameIndex = 0;
1148
+ this.timer = null;
1149
+ this.isTTY = !!process.stdout.isTTY;
1150
+ this.renderedLineCount = 0;
1151
+ this.stopped = false;
1152
+ this.hasPendingTasks = !allRunning;
1153
+ this.tasks = branches.map((branch, i) => ({
1154
+ index: i + 1,
1155
+ branch,
1156
+ path: paths[i],
1157
+ status: allRunning ? "running" : "pending",
1158
+ startedAt: allRunning ? now : 0,
1159
+ finishedAt: null,
1160
+ lastActiveAt: allRunning ? now : 0,
1161
+ durationMs: null,
1162
+ costUsd: null
1163
+ }));
1164
+ }
1165
+ /**
1166
+ * 启动定时渲染循环
1167
+ * TTY 模式下每 SPINNER_INTERVAL_MS 毫秒刷新一次面板
1168
+ * 非 TTY 模式下输出状态为 running 的任务的启动信息
1169
+ */
1170
+ start() {
1171
+ if (this.stopped) return;
1172
+ if (!this.isTTY) {
1173
+ for (const task of this.tasks) {
1174
+ if (task.status === "running") {
1175
+ console.log(MESSAGES.PROGRESS_TASK_STARTED(task.index, this.total, task.branch, task.path));
1176
+ }
943
1177
  }
944
- printInfo("");
1178
+ return;
1179
+ }
1180
+ process.stdout.write(CURSOR_HIDE);
1181
+ this.render();
1182
+ this.timer = setInterval(() => {
1183
+ this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
1184
+ this.render();
1185
+ }, SPINNER_INTERVAL_MS);
1186
+ if (this.timer.unref) {
1187
+ this.timer.unref();
945
1188
  }
946
- printInfo(`\u5171 ${worktrees.length} \u4E2A worktree`);
947
1189
  }
948
- }
949
-
950
- // src/commands/create.ts
951
- function registerCreateCommand(program2) {
952
- 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) => {
953
- handleCreate(options);
954
- });
955
- }
956
- function handleCreate(options) {
957
- validateMainWorktree();
958
- const count = Number(options.number);
959
- if (!Number.isInteger(count) || count <= 0) {
960
- throw new ClawtError(
961
- MESSAGES.INVALID_COUNT(options.number),
962
- EXIT_CODES.ARGUMENT_ERROR
963
- );
1190
+ /**
1191
+ * 更新指定任务的最后活动时间戳
1192
+ * 当 child.stderr 有输出时调用,表示任务仍然活跃
1193
+ * @param {number} index - 任务索引(从 0 开始)
1194
+ */
1195
+ updateActivity(index) {
1196
+ this.tasks[index].lastActiveAt = Date.now();
964
1197
  }
965
- logger.info(`create \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch}\uFF0C\u6570\u91CF: ${count}`);
966
- const worktrees = createWorktrees(options.branch, count);
967
- printSuccess(MESSAGES.WORKTREE_CREATED(worktrees.length));
968
- printInfo("");
969
- worktrees.forEach((wt, index) => {
970
- printInfo(`\u76EE\u5F55\u8DEF\u5F84${index + 1}\uFF1A`);
971
- printInfo(` ${wt.path}`);
972
- printInfo(` \u5206\u652F\u540D: ${wt.branch}`);
973
- printSeparator();
974
- });
975
- }
976
-
977
- // src/commands/remove.ts
978
- function registerRemoveCommand(program2) {
979
- 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\u5B8C\u6574\u5206\u652F\u540D\u7CBE\u786E\u5339\u914D\uFF09").action(async (options) => {
980
- await handleRemove(options);
981
- });
982
- }
983
- function resolveWorktreesToRemove(options) {
984
- const allWorktrees = getProjectWorktrees();
985
- if (options.all) {
986
- return allWorktrees;
1198
+ /**
1199
+ * 标记指定任务为运行中状态
1200
+ * 将 pending 任务标记为 running 并设置启动时间戳
1201
+ * @param {number} index - 任务索引(从 0 开始)
1202
+ */
1203
+ markRunning(index) {
1204
+ const task = this.tasks[index];
1205
+ const now = Date.now();
1206
+ task.status = "running";
1207
+ task.startedAt = now;
1208
+ task.lastActiveAt = now;
1209
+ if (!this.isTTY) {
1210
+ console.log(MESSAGES.PROGRESS_TASK_STARTED(task.index, this.total, task.branch, task.path));
1211
+ }
987
1212
  }
988
- if (!options.branch) {
989
- throw new ClawtError("\u8BF7\u6307\u5B9A --all \u6216 -b <branchName> \u53C2\u6570");
1213
+ /**
1214
+ * 标记指定任务为完成状态
1215
+ * @param {number} index - 任务索引(从 0 开始)
1216
+ * @param {number} durationMs - 耗时(毫秒)
1217
+ * @param {number} costUsd - 费用(美元)
1218
+ */
1219
+ markDone(index, durationMs, costUsd) {
1220
+ const task = this.tasks[index];
1221
+ task.status = "done";
1222
+ task.finishedAt = Date.now();
1223
+ task.durationMs = durationMs;
1224
+ task.costUsd = costUsd;
1225
+ if (!this.isTTY) {
1226
+ const duration = formatDuration(durationMs);
1227
+ const cost = `$${costUsd.toFixed(2)}`;
1228
+ console.log(MESSAGES.PROGRESS_TASK_DONE(task.index, this.total, task.branch, duration, cost, task.path));
1229
+ }
990
1230
  }
991
- const matched = allWorktrees.filter(
992
- (wt) => wt.branch === options.branch || wt.branch.startsWith(`${options.branch}-`)
993
- );
994
- if (matched.length === 0) {
995
- throw new ClawtError(MESSAGES.WORKTREE_NOT_FOUND(options.branch));
1231
+ /**
1232
+ * 标记指定任务为失败状态
1233
+ * @param {number} index - 任务索引(从 0 开始)
1234
+ * @param {number} durationMs - 耗时(毫秒)
1235
+ */
1236
+ markFailed(index, durationMs) {
1237
+ const task = this.tasks[index];
1238
+ task.status = "failed";
1239
+ task.finishedAt = Date.now();
1240
+ task.durationMs = durationMs;
1241
+ if (!this.isTTY) {
1242
+ const duration = formatDuration(durationMs);
1243
+ console.log(MESSAGES.PROGRESS_TASK_FAILED(task.index, this.total, task.branch, duration, task.path));
1244
+ }
996
1245
  }
997
- return matched;
998
- }
999
- async function handleRemove(options) {
1000
- validateMainWorktree();
1001
- const projectName = getProjectName();
1002
- logger.info(`remove \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}`);
1003
- const worktreesToRemove = resolveWorktreesToRemove(options);
1004
- if (worktreesToRemove.length === 0) {
1005
- printInfo(MESSAGES.NO_WORKTREES);
1006
- return;
1246
+ /**
1247
+ * 停止渲染循环并恢复光标
1248
+ * 在所有任务完成或 SIGINT 中断时调用
1249
+ */
1250
+ stop() {
1251
+ if (this.stopped) return;
1252
+ this.stopped = true;
1253
+ if (this.timer) {
1254
+ clearInterval(this.timer);
1255
+ this.timer = null;
1256
+ }
1257
+ if (this.isTTY) {
1258
+ this.render();
1259
+ process.stdout.write(CURSOR_SHOW);
1260
+ }
1007
1261
  }
1008
- printInfo("\u5373\u5C06\u79FB\u9664\u4EE5\u4E0B worktree \u53CA\u672C\u5730\u5206\u652F\uFF1A\n");
1009
- worktreesToRemove.forEach((wt, index) => {
1010
- printInfo(` ${index + 1}. ${wt.path} \u2192 \u5206\u652F: ${wt.branch}`);
1011
- });
1012
- printInfo("");
1013
- const autoDelete = getConfigValue("autoDeleteBranch");
1014
- let shouldDeleteBranch = autoDelete;
1015
- if (!autoDelete) {
1016
- shouldDeleteBranch = await confirmAction("\u662F\u5426\u540C\u65F6\u5220\u9664\u5BF9\u5E94\u7684\u672C\u5730\u5206\u652F\uFF1F");
1262
+ /**
1263
+ * 执行一次完整的面板渲染
1264
+ * 先回退光标到面板起始位置,再逐行输出
1265
+ */
1266
+ render() {
1267
+ const maxBranchWidth = getMaxBranchWidth(this.tasks);
1268
+ const spinnerChar = SPINNER_FRAMES[this.frameIndex];
1269
+ const lines = this.tasks.map((task) => renderTaskLine(task, this.total, maxBranchWidth, spinnerChar));
1270
+ if (this.hasPendingTasks) {
1271
+ lines.push(renderSummaryLine(this.tasks, this.total));
1272
+ }
1273
+ if (this.renderedLineCount > 0) {
1274
+ process.stdout.write(CURSOR_UP(this.renderedLineCount));
1275
+ }
1276
+ for (const line of lines) {
1277
+ process.stdout.write(`${line}${CLEAR_LINE}
1278
+ `);
1279
+ }
1280
+ this.renderedLineCount = lines.length;
1017
1281
  }
1018
- const failures = [];
1019
- for (const wt of worktreesToRemove) {
1020
- try {
1021
- removeWorktreeByPath(wt.path);
1022
- if (shouldDeleteBranch) {
1023
- deleteBranch(wt.branch);
1282
+ };
1283
+
1284
+ // src/utils/task-file.ts
1285
+ import { resolve } from "path";
1286
+ import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
1287
+ var TASK_BLOCK_REGEX = /<!-- CLAWT-TASKS:START -->([\s\S]*?)<!-- CLAWT-TASKS:END -->/g;
1288
+ var BRANCH_LINE_REGEX = /^#\s*branch:\s*(.+)$/;
1289
+ function parseTaskFile(content, options) {
1290
+ const branchRequired = options?.branchRequired ?? true;
1291
+ const entries = [];
1292
+ let match;
1293
+ let blockIndex = 0;
1294
+ TASK_BLOCK_REGEX.lastIndex = 0;
1295
+ while ((match = TASK_BLOCK_REGEX.exec(content)) !== null) {
1296
+ blockIndex++;
1297
+ const blockContent = match[1].trim();
1298
+ const lines = blockContent.split("\n");
1299
+ let branch;
1300
+ const taskLines = [];
1301
+ for (const line of lines) {
1302
+ const branchMatch = line.trim().match(BRANCH_LINE_REGEX);
1303
+ if (branchMatch) {
1304
+ branch = branchMatch[1].trim();
1305
+ } else {
1306
+ taskLines.push(line);
1024
1307
  }
1025
- removeSnapshot(projectName, wt.branch);
1026
- printSuccess(MESSAGES.WORKTREE_REMOVED(wt.path));
1027
- } catch (error) {
1028
- const errorMessage = error instanceof Error ? error.message : String(error);
1029
- logger.error(`\u79FB\u9664 worktree \u5931\u8D25: ${wt.path} - ${error}`);
1030
- failures.push({ path: wt.path, error: errorMessage });
1031
1308
  }
1309
+ if (branchRequired && !branch) {
1310
+ throw new ClawtError(MESSAGES.TASK_FILE_MISSING_BRANCH(blockIndex));
1311
+ }
1312
+ const task = taskLines.join("\n").trim();
1313
+ if (!task) {
1314
+ throw new ClawtError(
1315
+ branch ? MESSAGES.TASK_FILE_MISSING_TASK(branch) : MESSAGES.TASK_FILE_MISSING_TASK_BY_INDEX(blockIndex)
1316
+ );
1317
+ }
1318
+ entries.push({ branch, task });
1032
1319
  }
1033
- if (options.all) {
1034
- removeProjectSnapshots(projectName);
1320
+ return entries;
1321
+ }
1322
+ function loadTaskFile(filePath, options) {
1323
+ const absolutePath = resolve(filePath);
1324
+ if (!existsSync6(absolutePath)) {
1325
+ throw new ClawtError(MESSAGES.TASK_FILE_NOT_FOUND(absolutePath));
1035
1326
  }
1036
- gitWorktreePrune();
1037
- const projectDir = getProjectWorktreeDir();
1038
- removeEmptyDir(projectDir);
1039
- if (failures.length > 0) {
1040
- printError(MESSAGES.REMOVE_PARTIAL_FAILURE(failures));
1041
- throw new ClawtError(
1042
- `${failures.length} \u4E2A worktree \u79FB\u9664\u5931\u8D25`
1043
- );
1327
+ const content = readFileSync3(absolutePath, "utf-8");
1328
+ const entries = parseTaskFile(content, options);
1329
+ if (entries.length === 0) {
1330
+ throw new ClawtError(MESSAGES.TASK_FILE_EMPTY);
1044
1331
  }
1332
+ return entries;
1045
1333
  }
1046
1334
 
1047
- // src/commands/run.ts
1048
- function registerRunCommand(program2) {
1049
- program2.command("run").description("\u6279\u91CF\u521B\u5EFA worktree \u5E76\u542F\u52A8 Claude Code \u6267\u884C\u4EFB\u52A1").requiredOption("-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").action(async (options) => {
1050
- await handleRun(options);
1051
- });
1052
- }
1335
+ // src/utils/task-executor.ts
1053
1336
  function executeClaudeTask(worktree, task) {
1054
1337
  const child = spawnProcess(
1055
1338
  "claude",
@@ -1063,7 +1346,7 @@ function executeClaudeTask(worktree, task) {
1063
1346
  stdio: ["ignore", "pipe", "pipe"]
1064
1347
  }
1065
1348
  );
1066
- const promise = new Promise((resolve) => {
1349
+ const promise = new Promise((resolve2) => {
1067
1350
  let stdout = "";
1068
1351
  let stderr = "";
1069
1352
  child.stdout?.on("data", (data) => {
@@ -1083,7 +1366,7 @@ function executeClaudeTask(worktree, task) {
1083
1366
  } catch {
1084
1367
  logger.warn(`\u89E3\u6790 Claude Code \u8F93\u51FA\u5931\u8D25: ${stdout.substring(0, 200)}`);
1085
1368
  }
1086
- resolve({
1369
+ resolve2({
1087
1370
  task,
1088
1371
  branch: worktree.branch,
1089
1372
  worktreePath: worktree.path,
@@ -1093,7 +1376,7 @@ function executeClaudeTask(worktree, task) {
1093
1376
  });
1094
1377
  });
1095
1378
  child.on("error", (err) => {
1096
- resolve({
1379
+ resolve2({
1097
1380
  task,
1098
1381
  branch: worktree.branch,
1099
1382
  worktreePath: worktree.path,
@@ -1105,24 +1388,6 @@ function executeClaudeTask(worktree, task) {
1105
1388
  });
1106
1389
  return { child, promise };
1107
1390
  }
1108
- function printTaskNotification(taskResult) {
1109
- const { success, worktreePath, branch, result } = taskResult;
1110
- const status = success ? "\u5B8C\u6210" : "\u5931\u8D25";
1111
- const icon = success ? "\u2713" : "\u2717";
1112
- const durationStr = result ? `${(result.duration_ms / 1e3).toFixed(1)}s` : "N/A";
1113
- const costStr = result ? `$${result.total_cost_usd.toFixed(2)}` : "N/A";
1114
- const resultStr = success ? "success" : "failed";
1115
- if (success) {
1116
- printSuccess(`${icon} [${status}] worktree: ${worktreePath}`);
1117
- } else {
1118
- printError(`${icon} [${status}] worktree: ${worktreePath}`);
1119
- }
1120
- printInfo(` \u5206\u652F: ${branch}`);
1121
- printInfo(` \u8017\u65F6: ${durationStr}`);
1122
- printInfo(` \u82B1\u8D39: ${costStr}`);
1123
- printInfo(` \u7ED3\u679C: ${resultStr}`);
1124
- printSeparator();
1125
- }
1126
1391
  function printTaskSummary(summary) {
1127
1392
  printDoubleSeparator();
1128
1393
  printInfo(`\u5168\u90E8\u4EFB\u52A1\u5DF2\u5B8C\u6210 (${summary.total}/${summary.total})`);
@@ -1147,60 +1412,117 @@ async function handleInterruptCleanup(worktrees) {
1147
1412
  printInfo(MESSAGES.INTERRUPT_KEPT);
1148
1413
  }
1149
1414
  }
1150
- async function handleRun(options) {
1151
- validateMainWorktree();
1152
- validateClaudeCodeInstalled();
1153
- if (!options.tasks || options.tasks.length === 0) {
1154
- const sanitized = sanitizeBranchName(options.branch);
1155
- if (checkBranchExists(sanitized)) {
1156
- throw new ClawtError(MESSAGES.BRANCH_EXISTS_USE_RESUME(sanitized));
1157
- }
1158
- const worktrees2 = createWorktrees(options.branch, 1);
1159
- const worktree = worktrees2[0];
1160
- printSuccess(MESSAGES.WORKTREE_CREATED(1));
1161
- launchInteractiveClaude(worktree);
1162
- return;
1163
- }
1164
- const tasks = options.tasks.map((t) => t.trim()).filter(Boolean);
1165
- if (tasks.length === 0) {
1166
- throw new ClawtError("\u4EFB\u52A1\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A");
1167
- }
1168
- const count = tasks.length;
1169
- logger.info(`run \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch}\uFF0C\u4EFB\u52A1\u6570: ${count}`);
1170
- const worktrees = createWorktrees(options.branch, count);
1171
- printSuccess(MESSAGES.WORKTREE_CREATED(worktrees.length));
1172
- for (const wt of worktrees) {
1173
- printInfo(` \u5206\u652F: ${wt.branch} \u8DEF\u5F84: ${wt.path}`);
1415
+ function updateRendererStatus(renderer, index, result, startTime) {
1416
+ if (result.success) {
1417
+ renderer.markDone(
1418
+ index,
1419
+ result.result?.duration_ms ?? Date.now() - startTime,
1420
+ result.result?.total_cost_usd ?? 0
1421
+ );
1422
+ } else {
1423
+ renderer.markFailed(
1424
+ index,
1425
+ result.result?.duration_ms ?? Date.now() - startTime
1426
+ );
1174
1427
  }
1175
- printInfo("");
1176
- const startTime = Date.now();
1428
+ }
1429
+ async function executeWithConcurrency(worktrees, tasks, concurrency, renderer, startTime, isInterrupted, childProcesses) {
1430
+ const total = tasks.length;
1431
+ const results = new Array(total);
1432
+ let nextIndex = 0;
1433
+ let completedCount = 0;
1434
+ return new Promise((resolve2) => {
1435
+ function launchNext() {
1436
+ if (nextIndex >= total || isInterrupted()) return;
1437
+ const index = nextIndex;
1438
+ nextIndex++;
1439
+ const wt = worktrees[index];
1440
+ const task = tasks[index];
1441
+ logger.info(`\u542F\u52A8\u4EFB\u52A1 ${index + 1}: ${task} (worktree: ${wt.path})`);
1442
+ renderer.markRunning(index);
1443
+ const handle = executeClaudeTask(wt, task);
1444
+ childProcesses.push(handle.child);
1445
+ handle.child.stderr?.on("data", () => {
1446
+ renderer.updateActivity(index);
1447
+ });
1448
+ handle.promise.then((result) => {
1449
+ results[index] = result;
1450
+ completedCount++;
1451
+ if (!isInterrupted()) {
1452
+ updateRendererStatus(renderer, index, result, startTime);
1453
+ }
1454
+ launchNext();
1455
+ if (completedCount === total) {
1456
+ resolve2(results);
1457
+ }
1458
+ });
1459
+ }
1460
+ const initialBatch = Math.min(concurrency, total);
1461
+ for (let i = 0; i < initialBatch; i++) {
1462
+ launchNext();
1463
+ }
1464
+ });
1465
+ }
1466
+ async function executeAllParallel(worktrees, tasks, renderer, startTime, isInterrupted, childProcesses) {
1177
1467
  const handles = worktrees.map((wt, index) => {
1178
1468
  const task = tasks[index];
1179
1469
  logger.info(`\u542F\u52A8\u4EFB\u52A1 ${index + 1}: ${task} (worktree: ${wt.path})`);
1180
- return executeClaudeTask(wt, task);
1470
+ const handle = executeClaudeTask(wt, task);
1471
+ childProcesses.push(handle.child);
1472
+ handle.child.stderr?.on("data", () => {
1473
+ renderer.updateActivity(index);
1474
+ });
1475
+ return handle;
1181
1476
  });
1182
- const childProcesses = handles.map((h) => h.child);
1477
+ const results = await Promise.all(
1478
+ handles.map(
1479
+ (handle, index) => handle.promise.then((result) => {
1480
+ if (!isInterrupted()) {
1481
+ updateRendererStatus(renderer, index, result, startTime);
1482
+ }
1483
+ return result;
1484
+ })
1485
+ )
1486
+ );
1487
+ return results;
1488
+ }
1489
+ async function executeBatchTasks(worktrees, tasks, concurrency) {
1490
+ const count = tasks.length;
1491
+ if (concurrency > 0) {
1492
+ printInfo(MESSAGES.CONCURRENCY_INFO(concurrency, count));
1493
+ printInfo("");
1494
+ }
1495
+ const startTime = Date.now();
1496
+ const branches = worktrees.map((wt) => wt.branch);
1497
+ const paths = worktrees.map((wt) => wt.path);
1498
+ const allRunning = concurrency === 0;
1499
+ const renderer = new ProgressRenderer(branches, paths, allRunning);
1500
+ renderer.start();
1183
1501
  let interrupted = false;
1502
+ const isInterrupted = () => interrupted;
1503
+ const childProcesses = [];
1184
1504
  const sigintHandler = async () => {
1185
1505
  if (interrupted) return;
1186
1506
  interrupted = true;
1507
+ renderer.stop();
1187
1508
  printInfo("");
1188
1509
  printWarning(MESSAGES.INTERRUPTED);
1189
1510
  killAllChildProcesses(childProcesses);
1190
- await Promise.allSettled(handles.map((h) => h.promise));
1511
+ await Promise.allSettled(childProcesses.map(
1512
+ (cp) => new Promise((resolve2) => {
1513
+ if (cp.exitCode !== null) {
1514
+ resolve2();
1515
+ } else {
1516
+ cp.on("close", () => resolve2());
1517
+ }
1518
+ })
1519
+ ));
1191
1520
  await handleInterruptCleanup(worktrees);
1192
1521
  process.exit(1);
1193
1522
  };
1194
1523
  process.on("SIGINT", sigintHandler);
1195
- const taskPromises = handles.map(
1196
- (handle) => handle.promise.then((result) => {
1197
- if (!interrupted) {
1198
- printTaskNotification(result);
1199
- }
1200
- return result;
1201
- })
1202
- );
1203
- const results = await Promise.all(taskPromises);
1524
+ const results = concurrency > 0 ? await executeWithConcurrency(worktrees, tasks, concurrency, renderer, startTime, isInterrupted, childProcesses) : await executeAllParallel(worktrees, tasks, renderer, startTime, isInterrupted, childProcesses);
1525
+ renderer.stop();
1204
1526
  process.removeListener("SIGINT", sigintHandler);
1205
1527
  if (interrupted) return;
1206
1528
  const totalDurationMs = Date.now() - startTime;
@@ -1214,6 +1536,216 @@ async function handleRun(options) {
1214
1536
  printTaskSummary(summary);
1215
1537
  }
1216
1538
 
1539
+ // src/commands/list.ts
1540
+ import chalk4 from "chalk";
1541
+ function registerListCommand(program2) {
1542
+ program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
1543
+ handleList(options);
1544
+ });
1545
+ }
1546
+ function handleList(options) {
1547
+ validateMainWorktree();
1548
+ const projectName = getProjectName();
1549
+ const worktrees = getProjectWorktrees();
1550
+ logger.info(`list \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}\uFF0C\u5171 ${worktrees.length} \u4E2A worktree`);
1551
+ if (options.json) {
1552
+ printListAsJson(projectName, worktrees);
1553
+ return;
1554
+ }
1555
+ printListAsText(projectName, worktrees);
1556
+ }
1557
+ function printListAsJson(projectName, worktrees) {
1558
+ const result = {
1559
+ project: projectName,
1560
+ total: worktrees.length,
1561
+ worktrees: worktrees.map((wt) => ({
1562
+ path: wt.path,
1563
+ branch: wt.branch
1564
+ }))
1565
+ };
1566
+ console.log(JSON.stringify(result, null, 2));
1567
+ }
1568
+ function printListAsText(projectName, worktrees) {
1569
+ printInfo(`\u5F53\u524D\u9879\u76EE: ${projectName}
1570
+ `);
1571
+ if (worktrees.length === 0) {
1572
+ printInfo(` ${MESSAGES.NO_WORKTREES}`);
1573
+ } else {
1574
+ for (const wt of worktrees) {
1575
+ const status = getWorktreeStatus(wt);
1576
+ const isIdle = status ? isWorktreeIdle(status) : false;
1577
+ const pathDisplay = isIdle ? chalk4.hex("#FF8C00")(wt.path) : wt.path;
1578
+ printInfo(` ${pathDisplay} [${wt.branch}]`);
1579
+ if (status) {
1580
+ printInfo(` ${formatWorktreeStatus(status)}`);
1581
+ } else {
1582
+ printInfo(` ${chalk4.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
1583
+ }
1584
+ printInfo("");
1585
+ }
1586
+ printInfo(`\u5171 ${worktrees.length} \u4E2A worktree`);
1587
+ }
1588
+ }
1589
+
1590
+ // src/commands/create.ts
1591
+ function registerCreateCommand(program2) {
1592
+ 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) => {
1593
+ handleCreate(options);
1594
+ });
1595
+ }
1596
+ function handleCreate(options) {
1597
+ validateMainWorktree();
1598
+ const count = Number(options.number);
1599
+ if (!Number.isInteger(count) || count <= 0) {
1600
+ throw new ClawtError(
1601
+ MESSAGES.INVALID_COUNT(options.number),
1602
+ EXIT_CODES.ARGUMENT_ERROR
1603
+ );
1604
+ }
1605
+ logger.info(`create \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch}\uFF0C\u6570\u91CF: ${count}`);
1606
+ const worktrees = createWorktrees(options.branch, count);
1607
+ printSuccess(MESSAGES.WORKTREE_CREATED(worktrees.length));
1608
+ printInfo("");
1609
+ worktrees.forEach((wt, index) => {
1610
+ printInfo(`\u76EE\u5F55\u8DEF\u5F84${index + 1}\uFF1A`);
1611
+ printInfo(` ${wt.path}`);
1612
+ printInfo(` \u5206\u652F\u540D: ${wt.branch}`);
1613
+ printSeparator();
1614
+ });
1615
+ }
1616
+
1617
+ // src/commands/remove.ts
1618
+ var REMOVE_RESOLVE_MESSAGES = {
1619
+ noWorktrees: MESSAGES.REMOVE_NO_WORKTREES,
1620
+ selectBranch: MESSAGES.REMOVE_SELECT_BRANCH,
1621
+ multipleMatches: MESSAGES.REMOVE_MULTIPLE_MATCHES,
1622
+ noMatch: MESSAGES.REMOVE_NO_MATCH
1623
+ };
1624
+ function registerRemoveCommand(program2) {
1625
+ 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) => {
1626
+ await handleRemove(options);
1627
+ });
1628
+ }
1629
+ async function handleRemove(options) {
1630
+ validateMainWorktree();
1631
+ const projectName = getProjectName();
1632
+ logger.info(`remove \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}`);
1633
+ const allWorktrees = getProjectWorktrees();
1634
+ let worktreesToRemove;
1635
+ if (options.all) {
1636
+ worktreesToRemove = allWorktrees;
1637
+ } else {
1638
+ worktreesToRemove = await resolveTargetWorktrees(allWorktrees, REMOVE_RESOLVE_MESSAGES, options.branch);
1639
+ }
1640
+ if (worktreesToRemove.length === 0) {
1641
+ printInfo(MESSAGES.NO_WORKTREES);
1642
+ return;
1643
+ }
1644
+ printInfo("\u5373\u5C06\u79FB\u9664\u4EE5\u4E0B worktree \u53CA\u672C\u5730\u5206\u652F\uFF1A\n");
1645
+ worktreesToRemove.forEach((wt, index) => {
1646
+ printInfo(` ${index + 1}. ${wt.path} \u2192 \u5206\u652F: ${wt.branch}`);
1647
+ });
1648
+ printInfo("");
1649
+ const autoDelete = getConfigValue("autoDeleteBranch");
1650
+ let shouldDeleteBranch = autoDelete;
1651
+ if (!autoDelete) {
1652
+ shouldDeleteBranch = await confirmAction("\u662F\u5426\u540C\u65F6\u5220\u9664\u5BF9\u5E94\u7684\u672C\u5730\u5206\u652F\uFF1F");
1653
+ }
1654
+ const failures = [];
1655
+ for (const wt of worktreesToRemove) {
1656
+ try {
1657
+ removeWorktreeByPath(wt.path);
1658
+ if (shouldDeleteBranch) {
1659
+ deleteBranch(wt.branch);
1660
+ }
1661
+ removeSnapshot(projectName, wt.branch);
1662
+ printSuccess(MESSAGES.WORKTREE_REMOVED(wt.path));
1663
+ } catch (error) {
1664
+ const errorMessage = error instanceof Error ? error.message : String(error);
1665
+ logger.error(`\u79FB\u9664 worktree \u5931\u8D25: ${wt.path} - ${error}`);
1666
+ failures.push({ path: wt.path, error: errorMessage });
1667
+ }
1668
+ }
1669
+ if (options.all) {
1670
+ removeProjectSnapshots(projectName);
1671
+ }
1672
+ gitWorktreePrune();
1673
+ const projectDir = getProjectWorktreeDir();
1674
+ removeEmptyDir(projectDir);
1675
+ if (failures.length > 0) {
1676
+ printError(MESSAGES.REMOVE_PARTIAL_FAILURE(failures));
1677
+ throw new ClawtError(
1678
+ `${failures.length} \u4E2A worktree \u79FB\u9664\u5931\u8D25`
1679
+ );
1680
+ }
1681
+ }
1682
+
1683
+ // src/commands/run.ts
1684
+ function registerRunCommand(program2) {
1685
+ 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").action(async (options) => {
1686
+ await handleRun(options);
1687
+ });
1688
+ }
1689
+ function parseConcurrency(optionValue, configValue) {
1690
+ if (optionValue === void 0) {
1691
+ return configValue;
1692
+ }
1693
+ const parsed = parseInt(optionValue, 10);
1694
+ if (Number.isNaN(parsed) || parsed < 0) {
1695
+ throw new ClawtError(MESSAGES.CONCURRENCY_INVALID);
1696
+ }
1697
+ return parsed;
1698
+ }
1699
+ async function handleRunFromFile(options) {
1700
+ const branchRequired = !options.branch;
1701
+ const entries = loadTaskFile(options.file, { branchRequired });
1702
+ printSuccess(MESSAGES.TASK_FILE_LOADED(entries.length, options.file));
1703
+ const tasks = entries.map((e) => e.task);
1704
+ let worktrees;
1705
+ if (options.branch) {
1706
+ worktrees = createWorktrees(options.branch, entries.length);
1707
+ } else {
1708
+ const branches = entries.map((e) => sanitizeBranchName(e.branch));
1709
+ worktrees = createWorktreesByBranches(branches);
1710
+ }
1711
+ const concurrency = parseConcurrency(options.concurrency, getConfigValue("maxConcurrency"));
1712
+ logger.info(`run \u547D\u4EE4\uFF08\u6587\u4EF6\u6A21\u5F0F\uFF09\u6267\u884C\uFF0C\u4EFB\u52A1\u6570: ${entries.length}\uFF0C\u5E76\u53D1\u6570: ${concurrency || "\u4E0D\u9650\u5236"}`);
1713
+ await executeBatchTasks(worktrees, tasks, concurrency);
1714
+ }
1715
+ async function handleRun(options) {
1716
+ validateMainWorktree();
1717
+ validateClaudeCodeInstalled();
1718
+ if (options.file && options.tasks) {
1719
+ throw new ClawtError(MESSAGES.FILE_AND_TASKS_CONFLICT);
1720
+ }
1721
+ if (options.file) {
1722
+ return handleRunFromFile(options);
1723
+ }
1724
+ if (!options.branch) {
1725
+ throw new ClawtError(MESSAGES.BRANCH_OR_FILE_REQUIRED);
1726
+ }
1727
+ if (!options.tasks || options.tasks.length === 0) {
1728
+ const sanitized = sanitizeBranchName(options.branch);
1729
+ if (checkBranchExists(sanitized)) {
1730
+ throw new ClawtError(MESSAGES.BRANCH_EXISTS_USE_RESUME(sanitized));
1731
+ }
1732
+ const worktrees2 = createWorktrees(options.branch, 1);
1733
+ const worktree = worktrees2[0];
1734
+ printSuccess(MESSAGES.WORKTREE_CREATED(1));
1735
+ launchInteractiveClaude(worktree);
1736
+ return;
1737
+ }
1738
+ const tasks = options.tasks.map((t) => t.trim()).filter(Boolean);
1739
+ if (tasks.length === 0) {
1740
+ throw new ClawtError("\u4EFB\u52A1\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A");
1741
+ }
1742
+ const count = tasks.length;
1743
+ const concurrency = parseConcurrency(options.concurrency, getConfigValue("maxConcurrency"));
1744
+ logger.info(`run \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch}\uFF0C\u4EFB\u52A1\u6570: ${count}\uFF0C\u5E76\u53D1\u6570: ${concurrency || "\u4E0D\u9650\u5236"}`);
1745
+ const worktrees = createWorktrees(options.branch, count);
1746
+ await executeBatchTasks(worktrees, tasks, concurrency);
1747
+ }
1748
+
1217
1749
  // src/commands/resume.ts
1218
1750
  var RESUME_RESOLVE_MESSAGES = {
1219
1751
  noWorktrees: MESSAGES.RESUME_NO_WORKTREES,
@@ -1538,7 +2070,7 @@ async function handleMerge(options) {
1538
2070
  }
1539
2071
 
1540
2072
  // src/commands/config.ts
1541
- import chalk4 from "chalk";
2073
+ import chalk5 from "chalk";
1542
2074
  function registerConfigCommand(program2) {
1543
2075
  const configCmd = program2.command("config").description("\u67E5\u770B\u548C\u7BA1\u7406\u5168\u5C40\u914D\u7F6E").action(() => {
1544
2076
  handleConfig();
@@ -1551,7 +2083,7 @@ function handleConfig() {
1551
2083
  const config = loadConfig();
1552
2084
  logger.info("config \u547D\u4EE4\u6267\u884C\uFF0C\u5C55\u793A\u5168\u5C40\u914D\u7F6E");
1553
2085
  printInfo(`
1554
- ${chalk4.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
2086
+ ${chalk5.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
1555
2087
  `);
1556
2088
  printSeparator();
1557
2089
  const keys = Object.keys(DEFAULT_CONFIG);
@@ -1561,8 +2093,8 @@ ${chalk4.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
1561
2093
  const description = CONFIG_DESCRIPTIONS[key];
1562
2094
  const formattedValue = formatConfigValue(value);
1563
2095
  if (i === 0) printInfo("");
1564
- printInfo(` ${chalk4.bold(key)}: ${formattedValue}`);
1565
- printInfo(` ${chalk4.dim(description)}`);
2096
+ printInfo(` ${chalk5.bold(key)}: ${formattedValue}`);
2097
+ printInfo(` ${chalk5.dim(description)}`);
1566
2098
  printInfo("");
1567
2099
  }
1568
2100
  printSeparator();
@@ -1582,9 +2114,9 @@ async function handleConfigReset() {
1582
2114
  }
1583
2115
  function formatConfigValue(value) {
1584
2116
  if (typeof value === "boolean") {
1585
- return value ? chalk4.green("true") : chalk4.yellow("false");
2117
+ return value ? chalk5.green("true") : chalk5.yellow("false");
1586
2118
  }
1587
- return chalk4.cyan(String(value));
2119
+ return chalk5.cyan(String(value));
1588
2120
  }
1589
2121
 
1590
2122
  // src/commands/sync.ts
@@ -1671,7 +2203,7 @@ async function handleReset() {
1671
2203
  }
1672
2204
 
1673
2205
  // src/commands/status.ts
1674
- import chalk5 from "chalk";
2206
+ import chalk6 from "chalk";
1675
2207
  function registerStatusCommand(program2) {
1676
2208
  program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
1677
2209
  handleStatus(options);
@@ -1767,7 +2299,7 @@ function printStatusAsJson(result) {
1767
2299
  }
1768
2300
  function printStatusAsText(result) {
1769
2301
  printDoubleSeparator();
1770
- printInfo(` ${chalk5.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
2302
+ printInfo(` ${chalk6.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
1771
2303
  printDoubleSeparator();
1772
2304
  printInfo("");
1773
2305
  printMainSection(result.main);
@@ -1780,17 +2312,17 @@ function printStatusAsText(result) {
1780
2312
  printDoubleSeparator();
1781
2313
  }
1782
2314
  function printMainSection(main) {
1783
- printInfo(` ${chalk5.bold("\u25C6")} ${chalk5.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
1784
- printInfo(` \u5206\u652F: ${chalk5.bold(main.branch)}`);
2315
+ printInfo(` ${chalk6.bold("\u25C6")} ${chalk6.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
2316
+ printInfo(` \u5206\u652F: ${chalk6.bold(main.branch)}`);
1785
2317
  if (main.isClean) {
1786
- printInfo(` \u72B6\u6001: ${chalk5.green("\u2713 \u5E72\u51C0")}`);
2318
+ printInfo(` \u72B6\u6001: ${chalk6.green("\u2713 \u5E72\u51C0")}`);
1787
2319
  } else {
1788
- printInfo(` \u72B6\u6001: ${chalk5.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
2320
+ printInfo(` \u72B6\u6001: ${chalk6.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
1789
2321
  }
1790
2322
  printInfo("");
1791
2323
  }
1792
2324
  function printWorktreesSection(worktrees, total) {
1793
- printInfo(` ${chalk5.bold("\u25C6")} ${chalk5.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
2325
+ printInfo(` ${chalk6.bold("\u25C6")} ${chalk6.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
1794
2326
  printInfo("");
1795
2327
  if (worktrees.length === 0) {
1796
2328
  printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
@@ -1802,39 +2334,39 @@ function printWorktreesSection(worktrees, total) {
1802
2334
  }
1803
2335
  function printWorktreeItem(wt) {
1804
2336
  const statusLabel = formatChangeStatusLabel(wt.changeStatus);
1805
- printInfo(` ${chalk5.bold("\u25CF")} ${chalk5.bold(wt.branch)} [${statusLabel}]`);
2337
+ printInfo(` ${chalk6.bold("\u25CF")} ${chalk6.bold(wt.branch)} [${statusLabel}]`);
1806
2338
  const parts = [];
1807
2339
  if (wt.insertions > 0 || wt.deletions > 0) {
1808
- parts.push(`${chalk5.green(`+${wt.insertions}`)} ${chalk5.red(`-${wt.deletions}`)}`);
2340
+ parts.push(`${chalk6.green(`+${wt.insertions}`)} ${chalk6.red(`-${wt.deletions}`)}`);
1809
2341
  }
1810
2342
  if (wt.commitsAhead > 0) {
1811
- parts.push(chalk5.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`));
2343
+ parts.push(chalk6.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`));
1812
2344
  }
1813
2345
  if (wt.commitsBehind > 0) {
1814
- parts.push(chalk5.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`));
2346
+ parts.push(chalk6.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`));
1815
2347
  } else {
1816
- parts.push(chalk5.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65"));
2348
+ parts.push(chalk6.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65"));
1817
2349
  }
1818
2350
  printInfo(` ${parts.join(" ")}`);
1819
2351
  if (wt.hasSnapshot) {
1820
- printInfo(` ${chalk5.blue("\u6709 validate \u5FEB\u7167")}`);
2352
+ printInfo(` ${chalk6.blue("\u6709 validate \u5FEB\u7167")}`);
1821
2353
  }
1822
2354
  printInfo("");
1823
2355
  }
1824
2356
  function formatChangeStatusLabel(status) {
1825
2357
  switch (status) {
1826
2358
  case "committed":
1827
- return chalk5.green(MESSAGES.STATUS_CHANGE_COMMITTED);
2359
+ return chalk6.green(MESSAGES.STATUS_CHANGE_COMMITTED);
1828
2360
  case "uncommitted":
1829
- return chalk5.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
2361
+ return chalk6.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
1830
2362
  case "conflict":
1831
- return chalk5.red(MESSAGES.STATUS_CHANGE_CONFLICT);
2363
+ return chalk6.red(MESSAGES.STATUS_CHANGE_CONFLICT);
1832
2364
  case "clean":
1833
- return chalk5.gray(MESSAGES.STATUS_CHANGE_CLEAN);
2365
+ return chalk6.gray(MESSAGES.STATUS_CHANGE_CLEAN);
1834
2366
  }
1835
2367
  }
1836
2368
  function printSnapshotsSection(snapshots) {
1837
- printInfo(` ${chalk5.bold("\u25C6")} ${chalk5.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.length} \u4E2A)`);
2369
+ printInfo(` ${chalk6.bold("\u25C6")} ${chalk6.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.length} \u4E2A)`);
1838
2370
  printInfo("");
1839
2371
  if (snapshots.length === 0) {
1840
2372
  printInfo(` ${MESSAGES.STATUS_NO_SNAPSHOTS}`);
@@ -1842,8 +2374,8 @@ function printSnapshotsSection(snapshots) {
1842
2374
  return;
1843
2375
  }
1844
2376
  for (const snap of snapshots) {
1845
- const orphanLabel = snap.worktreeExists ? "" : ` ${chalk5.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED)}`;
1846
- const icon = snap.worktreeExists ? chalk5.blue("\u25CF") : chalk5.yellow("\u26A0");
2377
+ const orphanLabel = snap.worktreeExists ? "" : ` ${chalk6.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED)}`;
2378
+ const icon = snap.worktreeExists ? chalk6.blue("\u25CF") : chalk6.yellow("\u26A0");
1847
2379
  printInfo(` ${icon} ${snap.branch}${orphanLabel}`);
1848
2380
  }
1849
2381
  printInfo("");