clawt 2.9.1 → 2.10.1

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 (48) hide show
  1. package/.claude/agent-memory/docs-sync-updater/MEMORY.md +14 -10
  2. package/.claude/agents/docs-sync-updater.md +11 -0
  3. package/README.md +63 -268
  4. package/dist/index.js +420 -103
  5. package/dist/postinstall.js +242 -0
  6. package/docs/spec.md +162 -14
  7. package/package.json +1 -1
  8. package/src/commands/remove.ts +21 -28
  9. package/src/commands/status.ts +327 -0
  10. package/src/constants/index.ts +1 -1
  11. package/src/constants/messages/common.ts +41 -0
  12. package/src/constants/messages/config.ts +5 -0
  13. package/src/constants/messages/create.ts +5 -0
  14. package/src/constants/messages/index.ts +29 -0
  15. package/src/constants/messages/merge.ts +42 -0
  16. package/src/constants/messages/remove.ts +15 -0
  17. package/src/constants/messages/reset.ts +7 -0
  18. package/src/constants/messages/resume.ts +12 -0
  19. package/src/constants/messages/run.ts +16 -0
  20. package/src/constants/messages/status.ts +25 -0
  21. package/src/constants/messages/sync.ts +24 -0
  22. package/src/constants/messages/validate.ts +25 -0
  23. package/src/constants/messages.ts +22 -0
  24. package/src/index.ts +2 -0
  25. package/src/types/command.ts +6 -0
  26. package/src/types/index.ts +2 -1
  27. package/src/types/status.ts +49 -0
  28. package/src/utils/git.ts +16 -0
  29. package/src/utils/index.ts +4 -3
  30. package/src/utils/validate-snapshot.ts +17 -0
  31. package/src/utils/worktree-matcher.ts +92 -0
  32. package/tests/unit/commands/config.test.ts +110 -0
  33. package/tests/unit/commands/create.test.ts +115 -0
  34. package/tests/unit/commands/list.test.ts +118 -0
  35. package/tests/unit/commands/merge.test.ts +323 -0
  36. package/tests/unit/commands/remove.test.ts +240 -0
  37. package/tests/unit/commands/reset.test.ts +124 -0
  38. package/tests/unit/commands/resume.test.ts +91 -0
  39. package/tests/unit/commands/run.test.ts +207 -0
  40. package/tests/unit/commands/status.test.ts +214 -0
  41. package/tests/unit/commands/sync.test.ts +208 -0
  42. package/tests/unit/commands/validate.test.ts +382 -0
  43. package/tests/unit/constants/messages.test.ts +1 -1
  44. package/tests/unit/utils/config.test.ts +21 -1
  45. package/tests/unit/utils/formatter.test.ts +44 -1
  46. package/tests/unit/utils/git.test.ts +44 -0
  47. package/tests/unit/utils/validate-snapshot.test.ts +25 -0
  48. package/tests/unit/utils/worktree-matcher.test.ts +81 -5
@@ -10,6 +10,248 @@ var LOGS_DIR = join(CLAWT_HOME, "logs");
10
10
  var WORKTREES_DIR = join(CLAWT_HOME, "worktrees");
11
11
  var VALIDATE_SNAPSHOTS_DIR = join(CLAWT_HOME, "validate-snapshots");
12
12
 
13
+ // src/constants/messages/common.ts
14
+ var COMMON_MESSAGES = {
15
+ /** 不在主 worktree 根目录 */
16
+ NOT_MAIN_WORKTREE: "\u8BF7\u5728\u4E3B worktree \u7684\u6839\u76EE\u5F55\u4E0B\u6267\u884C clawt",
17
+ /** Git 未安装 */
18
+ GIT_NOT_INSTALLED: "Git \u672A\u5B89\u88C5\u6216\u4E0D\u5728 PATH \u4E2D\uFF0C\u8BF7\u5148\u5B89\u88C5 Git",
19
+ /** Claude Code CLI 未安装 */
20
+ CLAUDE_NOT_INSTALLED: "Claude Code CLI \u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u5B89\u88C5\uFF1Anpm install -g @anthropic-ai/claude-code",
21
+ /** 分支已存在 */
22
+ BRANCH_EXISTS: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u65E0\u6CD5\u521B\u5EFA`,
23
+ /** 分支名清理后为空 */
24
+ BRANCH_NAME_EMPTY: (original) => `\u5206\u652F\u540D "${original}" \u4E2D\u4E0D\u5305\u542B\u5408\u6CD5\u5B57\u7B26\uFF0C\u65E0\u6CD5\u521B\u5EFA\u5206\u652F`,
25
+ /** 分支名被转换 */
26
+ BRANCH_SANITIZED: (original, sanitized) => `\u5206\u652F\u540D\u5DF2\u8F6C\u6362: ${original} \u2192 ${sanitized}`,
27
+ /** worktree 创建成功 */
28
+ WORKTREE_CREATED: (count) => `\u2713 \u5DF2\u521B\u5EFA ${count} \u4E2A worktree`,
29
+ /** worktree 移除成功 */
30
+ WORKTREE_REMOVED: (path) => `\u2713 \u5DF2\u79FB\u9664 worktree: ${path}`,
31
+ /** 没有 worktree */
32
+ NO_WORKTREES: "(\u65E0 worktree)",
33
+ /** 目标 worktree 不存在 */
34
+ WORKTREE_NOT_FOUND: (name) => `worktree ${name} \u4E0D\u5B58\u5728`,
35
+ /** 主 worktree 有未提交更改 */
36
+ MAIN_WORKTREE_DIRTY: "\u4E3B worktree \u6709\u672A\u63D0\u4EA4\u7684\u66F4\u6539\uFF0C\u8BF7\u5148\u5904\u7406",
37
+ /** 目标 worktree 无更改 */
38
+ TARGET_WORKTREE_CLEAN: "\u8BE5 worktree \u7684\u5206\u652F\u4E0A\u6CA1\u6709\u4EFB\u4F55\u66F4\u6539\uFF0C\u65E0\u9700\u9A8C\u8BC1",
39
+ /** 用户取消破坏性操作 */
40
+ DESTRUCTIVE_OP_CANCELLED: "\u5DF2\u53D6\u6D88\u64CD\u4F5C",
41
+ /** 请提供提交信息 */
42
+ COMMIT_MESSAGE_REQUIRED: "\u8BF7\u63D0\u4F9B\u63D0\u4EA4\u4FE1\u606F\uFF08-m \u53C2\u6570\uFF09",
43
+ /** 配置文件损坏,已重新生成默认配置 */
44
+ CONFIG_CORRUPTED: "\u914D\u7F6E\u6587\u4EF6\u635F\u574F\u6216\u65E0\u6CD5\u89E3\u6790\uFF0C\u5DF2\u91CD\u65B0\u751F\u6210\u9ED8\u8BA4\u914D\u7F6E",
45
+ /** worktree 状态获取失败 */
46
+ WORKTREE_STATUS_UNAVAILABLE: "(\u72B6\u6001\u4E0D\u53EF\u7528)",
47
+ /** 分隔线 */
48
+ 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",
49
+ /** 粗分隔线 */
50
+ 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"
51
+ };
52
+
53
+ // src/constants/messages/run.ts
54
+ var RUN_MESSAGES = {
55
+ /** 分支已存在时提示使用 resume */
56
+ BRANCH_EXISTS_USE_RESUME: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u4F7F\u7528 clawt resume -b ${name} \u6062\u590D\u4F1A\u8BDD`,
57
+ /** 检测到用户中断 */
58
+ INTERRUPTED: "\u68C0\u6D4B\u5230\u9000\u51FA\u6307\u4EE4\uFF0C\u5DF2\u505C\u6B62 Claude Code \u4EFB\u52A1",
59
+ /** 中断后自动清理完成 */
60
+ INTERRUPT_AUTO_CLEANED: (count) => `\u2713 \u5DF2\u81EA\u52A8\u6E05\u7406 ${count} \u4E2A worktree \u548C\u5BF9\u5E94\u5206\u652F`,
61
+ /** 中断后手动确认清理 */
62
+ INTERRUPT_CONFIRM_CLEANUP: "\u662F\u5426\u79FB\u9664\u521A\u521A\u521B\u5EFA\u7684 worktree \u548C\u5BF9\u5E94\u5206\u652F\uFF1F",
63
+ /** 中断后清理完成 */
64
+ INTERRUPT_CLEANED: (count) => `\u2713 \u5DF2\u6E05\u7406 ${count} \u4E2A worktree \u548C\u5BF9\u5E94\u5206\u652F`,
65
+ /** 中断后保留 worktree */
66
+ INTERRUPT_KEPT: "\u5DF2\u4FDD\u7559 worktree\uFF0C\u53EF\u7A0D\u540E\u4F7F\u7528 clawt remove \u624B\u52A8\u6E05\u7406"
67
+ };
68
+
69
+ // src/constants/messages/create.ts
70
+ var CREATE_MESSAGES = {
71
+ /** 创建数量参数无效 */
72
+ INVALID_COUNT: (value) => `\u65E0\u6548\u7684\u521B\u5EFA\u6570\u91CF: "${value}"\uFF0C\u8BF7\u8F93\u5165\u6B63\u6574\u6570`
73
+ };
74
+
75
+ // src/constants/messages/merge.ts
76
+ var MERGE_MESSAGES = {
77
+ /** merge 成功 */
78
+ MERGE_SUCCESS: (branch, message, pushed) => `\u2713 \u5206\u652F ${branch} \u5DF2\u6210\u529F\u5408\u5E76\u5230\u5F53\u524D\u5206\u652F
79
+ \u63D0\u4EA4\u4FE1\u606F: ${message}${pushed ? "\n \u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\u4ED3\u5E93" : ""}`,
80
+ /** merge 成功(无提交信息,目标 worktree 已提交过) */
81
+ 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" : ""}`,
82
+ /** merge 冲突 */
83
+ 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",
84
+ /** merge 后清理 worktree 和分支成功 */
85
+ WORKTREE_CLEANED: (branch) => `\u2713 \u5DF2\u6E05\u7406 worktree \u548C\u5206\u652F: ${branch}`,
86
+ /** 目标 worktree 有未提交修改但未指定 -m */
87
+ 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",
88
+ /** 目标 worktree 既干净又无本地提交 */
89
+ 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",
90
+ /** merge 命令检测到 validate 状态的提示 */
91
+ 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`,
92
+ /** merge 检测到 auto-save 提交,提示用户是否压缩 */
93
+ 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",
94
+ /** squash 完成且通过 -m 直接提交后的提示 */
95
+ MERGE_SQUASH_COMMITTED: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u6240\u6709\u63D0\u4EA4\u538B\u7F29\u4E3A\u4E00\u4E2A`,
96
+ /** squash 完成但未提供 -m,提示用户自行提交 */
97
+ MERGE_SQUASH_PENDING: (worktreePath, branch) => `\u2713 \u5DF2\u5C06\u6240\u6709\u63D0\u4EA4\u538B\u7F29\u5230\u6682\u5B58\u533A
98
+ \u8BF7\u5728\u76EE\u6807 worktree \u4E2D\u63D0\u4EA4\u540E\u91CD\u65B0\u6267\u884C merge\uFF1A
99
+ cd ${worktreePath}
100
+ \u63D0\u4EA4\u5B8C\u6210\u540E\u6267\u884C\uFF1Aclawt merge -b ${branch}`,
101
+ /** merge 后 pull 冲突 */
102
+ 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",
103
+ /** push 失败 */
104
+ PUSH_FAILED: "\u81EA\u52A8 push \u5931\u8D25\uFF0Cmerge \u548C pull \u5DF2\u5B8C\u6210\n \u8BF7\u624B\u52A8\u6267\u884C git push",
105
+ /** merge 无可用 worktree */
106
+ 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",
107
+ /** merge 模糊匹配无结果,列出可用分支 */
108
+ MERGE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
109
+ \u53EF\u7528\u5206\u652F\uFF1A
110
+ ${branches.map((b) => ` - ${b}`).join("\n")}`,
111
+ /** merge 交互选择提示 */
112
+ MERGE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u5408\u5E76\u7684\u5206\u652F",
113
+ /** merge 模糊匹配到多个结果提示 */
114
+ MERGE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
115
+ };
116
+
117
+ // src/constants/messages/validate.ts
118
+ var VALIDATE_MESSAGES = {
119
+ /** validate 成功 */
120
+ VALIDATE_SUCCESS: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u53D8\u66F4\u5E94\u7528\u5230\u4E3B worktree
121
+ \u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86`,
122
+ /** 增量 validate 成功提示 */
123
+ 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
124
+ \u6682\u5B58\u533A = \u4E0A\u6B21\u5FEB\u7167\uFF0C\u5DE5\u4F5C\u76EE\u5F55 = \u6700\u65B0\u53D8\u66F4`,
125
+ /** 增量 validate 降级为全量模式提示 */
126
+ INCREMENTAL_VALIDATE_FALLBACK: "\u589E\u91CF\u5BF9\u6BD4\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u5168\u91CF\u6A21\u5F0F",
127
+ /** validate 状态已清理 */
128
+ VALIDATE_CLEANED: (branch) => `\u2713 \u5206\u652F ${branch} \u7684 validate \u72B6\u6001\u5DF2\u6E05\u7406`,
129
+ /** validate patch apply 失败,提示用户同步主分支 */
130
+ VALIDATE_PATCH_APPLY_FAILED: (branch) => `\u53D8\u66F4\u8FC1\u79FB\u5931\u8D25\uFF1A\u76EE\u6807\u5206\u652F\u4E0E\u4E3B\u5206\u652F\u5DEE\u5F02\u8FC7\u5927
131
+ \u8BF7\u5148\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`,
132
+ /** validate 无可用 worktree */
133
+ 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",
134
+ /** validate 模糊匹配无结果,列出可用分支 */
135
+ VALIDATE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
136
+ \u53EF\u7528\u5206\u652F\uFF1A
137
+ ${branches.map((b) => ` - ${b}`).join("\n")}`,
138
+ /** validate 交互选择提示 */
139
+ VALIDATE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u9A8C\u8BC1\u7684\u5206\u652F",
140
+ /** validate 模糊匹配到多个结果提示 */
141
+ VALIDATE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
142
+ };
143
+
144
+ // src/constants/messages/sync.ts
145
+ var SYNC_MESSAGES = {
146
+ /** sync 自动保存未提交变更 */
147
+ SYNC_AUTO_COMMITTED: (branch) => `\u5DF2\u81EA\u52A8\u4FDD\u5B58 ${branch} \u5206\u652F\u7684\u672A\u63D0\u4EA4\u53D8\u66F4`,
148
+ /** sync 开始合并 */
149
+ SYNC_MERGING: (targetBranch, mainBranch) => `\u6B63\u5728\u5C06 ${mainBranch} \u5408\u5E76\u5230 ${targetBranch} ...`,
150
+ /** sync 成功 */
151
+ SYNC_SUCCESS: (targetBranch, mainBranch) => `\u2713 \u5DF2\u5C06 ${mainBranch} \u7684\u6700\u65B0\u4EE3\u7801\u540C\u6B65\u5230 ${targetBranch}`,
152
+ /** sync 冲突 */
153
+ SYNC_CONFLICT: (worktreePath) => `\u5408\u5E76\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u8FDB\u5165\u76EE\u6807 worktree \u624B\u52A8\u89E3\u51B3\uFF1A
154
+ cd ${worktreePath}
155
+ \u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue
156
+ clawt validate -b <branch> \u9A8C\u8BC1\u53D8\u66F4`,
157
+ /** sync 无可用 worktree */
158
+ 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",
159
+ /** sync 模糊匹配无结果,列出可用分支 */
160
+ SYNC_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
161
+ \u53EF\u7528\u5206\u652F\uFF1A
162
+ ${branches.map((b) => ` - ${b}`).join("\n")}`,
163
+ /** sync 交互选择提示 */
164
+ SYNC_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u540C\u6B65\u7684\u5206\u652F",
165
+ /** sync 模糊匹配到多个结果提示 */
166
+ SYNC_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
167
+ };
168
+
169
+ // src/constants/messages/resume.ts
170
+ var RESUME_MESSAGES = {
171
+ /** resume 无可用 worktree */
172
+ 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",
173
+ /** resume 模糊匹配无结果,列出可用分支 */
174
+ RESUME_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
175
+ \u53EF\u7528\u5206\u652F\uFF1A
176
+ ${branches.map((b) => ` - ${b}`).join("\n")}`,
177
+ /** resume 交互选择提示 */
178
+ RESUME_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u6062\u590D\u7684\u5206\u652F",
179
+ /** resume 模糊匹配到多个结果提示 */
180
+ RESUME_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
181
+ };
182
+
183
+ // src/constants/messages/remove.ts
184
+ var REMOVE_MESSAGES = {
185
+ /** remove 无可用 worktree */
186
+ REMOVE_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u65E0\u9700\u79FB\u9664",
187
+ /** remove 多选交互提示 */
188
+ REMOVE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u79FB\u9664\u7684\u5206\u652F\uFF08\u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
189
+ /** remove 模糊匹配到多个结果提示 */
190
+ 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`,
191
+ /** remove 模糊匹配无结果,列出可用分支 */
192
+ REMOVE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
193
+ \u53EF\u7528\u5206\u652F\uFF1A
194
+ ${branches.map((b) => ` - ${b}`).join("\n")}`,
195
+ /** 批量移除部分失败 */
196
+ REMOVE_PARTIAL_FAILURE: (failures) => `\u4EE5\u4E0B worktree \u79FB\u9664\u5931\u8D25\uFF1A
197
+ ${failures.map((f) => ` \u2717 ${f.path}: ${f.error}`).join("\n")}`
198
+ };
199
+
200
+ // src/constants/messages/reset.ts
201
+ var RESET_MESSAGES = {
202
+ /** reset 成功 */
203
+ RESET_SUCCESS: "\u2713 \u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u91CD\u7F6E",
204
+ /** reset 时工作区和暂存区已干净 */
205
+ RESET_ALREADY_CLEAN: "\u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u662F\u5E72\u51C0\u72B6\u6001\uFF0C\u65E0\u9700\u91CD\u7F6E"
206
+ };
207
+
208
+ // src/constants/messages/config.ts
209
+ var CONFIG_CMD_MESSAGES = {
210
+ /** 配置已恢复为默认值 */
211
+ CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C"
212
+ };
213
+
214
+ // src/constants/messages/status.ts
215
+ var STATUS_MESSAGES = {
216
+ /** status 命令标题 */
217
+ STATUS_TITLE: (projectName) => `\u9879\u76EE\u72B6\u6001\u603B\u89C8: ${projectName}`,
218
+ /** status 主 worktree 区块标题 */
219
+ STATUS_MAIN_SECTION: "\u4E3B Worktree",
220
+ /** status worktrees 区块标题 */
221
+ STATUS_WORKTREES_SECTION: "Worktree \u5217\u8868",
222
+ /** status 快照区块标题 */
223
+ STATUS_SNAPSHOTS_SECTION: "\u672A\u6E05\u7406\u7684 Validate \u5FEB\u7167",
224
+ /** status 无 worktree */
225
+ STATUS_NO_WORKTREES: "(\u65E0\u6D3B\u8DC3 worktree)",
226
+ /** status 无未清理快照 */
227
+ STATUS_NO_SNAPSHOTS: "(\u65E0\u672A\u6E05\u7406\u7684\u5FEB\u7167)",
228
+ /** status 变更状态:已提交 */
229
+ STATUS_CHANGE_COMMITTED: "\u5DF2\u63D0\u4EA4",
230
+ /** status 变更状态:未提交修改 */
231
+ STATUS_CHANGE_UNCOMMITTED: "\u672A\u63D0\u4EA4\u4FEE\u6539",
232
+ /** status 变更状态:合并冲突 */
233
+ STATUS_CHANGE_CONFLICT: "\u5408\u5E76\u51B2\u7A81",
234
+ /** status 变更状态:无变更 */
235
+ STATUS_CHANGE_CLEAN: "\u65E0\u53D8\u66F4",
236
+ /** status 快照对应 worktree 已不存在 */
237
+ STATUS_SNAPSHOT_ORPHANED: "(\u5BF9\u5E94 worktree \u5DF2\u4E0D\u5B58\u5728)"
238
+ };
239
+
240
+ // src/constants/messages/index.ts
241
+ var MESSAGES = {
242
+ ...COMMON_MESSAGES,
243
+ ...RUN_MESSAGES,
244
+ ...CREATE_MESSAGES,
245
+ ...MERGE_MESSAGES,
246
+ ...VALIDATE_MESSAGES,
247
+ ...SYNC_MESSAGES,
248
+ ...RESUME_MESSAGES,
249
+ ...REMOVE_MESSAGES,
250
+ ...RESET_MESSAGES,
251
+ ...CONFIG_CMD_MESSAGES,
252
+ ...STATUS_MESSAGES
253
+ };
254
+
13
255
  // src/constants/config.ts
14
256
  var CONFIG_DEFINITIONS = {
15
257
  autoDeleteBranch: {
package/docs/spec.md CHANGED
@@ -25,6 +25,7 @@
25
25
  - [5.11 在已有 Worktree 中恢复会话](#511-在已有-worktree-中恢复会话)
26
26
  - [5.12 将主分支代码同步到目标 Worktree](#512-将主分支代码同步到目标-worktree)
27
27
  - [5.13 重置主 Worktree 工作区和暂存区](#513-重置主-worktree-工作区和暂存区)
28
+ - [5.14 项目全局状态总览](#514-项目全局状态总览)
28
29
  - [6. 错误处理规范](#6-错误处理规范)
29
30
  - [7. 非功能性需求](#7-非功能性需求)
30
31
  - [7.1 性能](#71-性能)
@@ -170,13 +171,14 @@ git show-ref --verify refs/heads/<branchName> 2>/dev/null
170
171
  | `clawt run` | 批量创建 worktree + 启动 Claude Code 执行任务 | 5.2 |
171
172
  | `clawt validate` | 在主 worktree 验证某个 worktree 分支的变更 | 5.4 |
172
173
  | `clawt merge` | 合并某个已验证的 worktree 分支到主 worktree | 5.6 |
173
- | `clawt remove` | 移除 worktree(支持单个/批量/全部) | 5.5 |
174
+ | `clawt remove` | 移除 worktree(支持模糊匹配/多选/全部) | 5.5 |
174
175
  | `clawt list` | 列出当前项目所有 worktree(支持 `--json` 格式输出) | 5.8 |
175
176
  | `clawt config` | 查看全局配置 | 5.10 |
176
177
  | `clawt config reset` | 将配置恢复为默认值 | 5.10 |
177
178
  | `clawt resume` | 在已有 worktree 中恢复 Claude Code 交互式会话 | 5.11 |
178
179
  | `clawt sync` | 将主分支最新代码同步到目标 worktree | 5.12 |
179
180
  | `clawt reset` | 重置主 worktree 工作区和暂存区 | 5.13 |
181
+ | `clawt status` | 显示项目全局状态总览(支持 `--json` 格式输出) | 5.14 |
180
182
 
181
183
  **全局选项:**
182
184
 
@@ -572,29 +574,42 @@ git apply --cached < patch
572
574
  **命令:**
573
575
 
574
576
  ```bash
575
- clawt remove [options]
577
+ # 移除当前项目所有 worktree
578
+ clawt remove --all
579
+
580
+ # 指定分支名(支持模糊匹配)
581
+ clawt remove -b <branchName>
582
+
583
+ # 不指定参数(列出所有分支供多选)
584
+ clawt remove
576
585
  ```
577
586
 
578
587
  **参数:**
579
588
 
580
- | 参数 | 说明 |
581
- | --------- | ---------------------------------------------------------- |
582
- | `--all` | 移除当前项目 (`~/.clawt/worktrees/<project>/`) 下所有 worktree |
583
- | `-b <branchName>` | 移除匹配 branchName 或 branchName-* 的 worktree |
589
+ | 参数 | 必填 | 说明 |
590
+ | --------- | ---- | ---------------------------------------------------------------------- |
591
+ | `--all` | 否 | 移除当前项目 (`~/.clawt/worktrees/<project>/`) 下所有 worktree |
592
+ | `-b` | | 指定分支名(支持模糊匹配,不传则列出所有分支供多选) |
584
593
 
585
- **三种移除粒度:**
586
-
587
- | 粒度 | 命令示例 | 移除范围 |
588
- | ---- | ---------------------------------------- | ------------------------------------------------------------- |
589
- | 全部 | `clawt remove --all` | `~/.clawt/worktrees/<project>/` 下所有 worktree |
590
- | 分支 | `clawt remove -b feature-scheme` | `~/.clawt/worktrees/<project>/feature-scheme-*` 的所有 worktree |
591
- | 单个 | `clawt remove -b feature-scheme-2` | 仅移除 `feature-scheme-2` 对应的 worktree(完整分支名精确匹配) |
594
+ > **提示:** 不传 `--all` 也不传 `-b` 时,会列出当前项目所有 worktree 供交互式多选。
592
595
 
593
596
  **运行流程:**
594
597
 
595
598
  1. **主 worktree 校验** (2.1)
596
599
  2. **获取项目名** (2.2)
597
- 3. 根据参数确定要移除的 worktree 列表
600
+ 3. **确定待移除的 worktree 列表**:
601
+ - **指定 `--all`** → 选中当前项目所有 worktree
602
+ - **未指定 `--all`** → 通过 `resolveTargetWorktrees` 解析目标 worktree(多选版本),匹配策略如下:
603
+ - **未传 `-b` 参数**:
604
+ - 无可用 worktree → 报错退出
605
+ - 仅 1 个 worktree → 直接使用,无需选择
606
+ - 多个 worktree → 通过交互式多选列表(Enquirer.MultiSelect)让用户选择(空格选择,回车确认)
607
+ - **传了 `-b` 参数**:
608
+ 1. **精确匹配优先**:在 worktree 列表中查找分支名完全相同的 worktree,找到则直接使用
609
+ 2. **模糊匹配**(子串匹配,大小写不敏感):
610
+ - 唯一匹配 → 直接使用
611
+ - 多个匹配 → 通过交互式多选列表让用户从匹配结果中选择
612
+ 3. **无匹配** → 报错退出,并列出所有可用分支名
598
613
  4. 列出即将移除的 worktree 及对应分支:
599
614
 
600
615
  ```
@@ -1122,6 +1137,139 @@ clawt reset
1122
1137
 
1123
1138
  ---
1124
1139
 
1140
+ ### 5.14 项目全局状态总览
1141
+
1142
+ **命令:**
1143
+
1144
+ ```bash
1145
+ clawt status [--json]
1146
+ ```
1147
+
1148
+ **参数:**
1149
+
1150
+ | 参数 | 必填 | 说明 |
1151
+ | -------- | ---- | ---------------------------------------- |
1152
+ | `--json` | 否 | 以 JSON 格式输出完整状态数据 |
1153
+
1154
+ **使用场景:**
1155
+
1156
+ 在管理多个 worktree 时,快速了解项目全局状态:主 worktree 当前分支及干净状态、所有 worktree 的变更情况和与主分支的同步状态、未清理的 validate 快照。
1157
+
1158
+ **运行流程:**
1159
+
1160
+ 1. **主 worktree 校验** (2.1)
1161
+ 2. **收集主 worktree 状态**:
1162
+ - 获取当前分支名(`getCurrentBranch()`)
1163
+ - 检测工作区是否干净(`isWorkingDirClean()`)
1164
+ - 获取项目名(`getProjectName()`)
1165
+ 3. **收集各 worktree 详细状态**:
1166
+ - 获取项目所有 worktree(`getProjectWorktrees()`)
1167
+ - 对每个 worktree 收集以下信息:
1168
+ - **变更状态**(优先级:合并冲突 > 未提交修改 > 已提交 > 无变更)
1169
+ - **行数差异**(新增/删除行数,通过 `getDiffStat()` 获取)
1170
+ - **提交差异**(相对于主分支的领先提交数 `getCommitCountAhead()` 和落后提交数 `getCommitCountBehind()`)
1171
+ - **快照状态**(是否存在 validate 快照)
1172
+ 4. **收集未清理的 validate 快照**:
1173
+ - 通过 `getProjectSnapshotBranches()` 扫描快照目录下的 `.tree` 文件获取所有存在快照的分支名
1174
+ - 对比现有 worktree 分支列表,标识孤立快照(对应 worktree 已不存在的快照)
1175
+ 5. **输出状态信息**:
1176
+ - 指定 `--json` → 以 JSON 格式输出完整状态数据(`JSON.stringify`)
1177
+ - 未指定 → 以文本格式输出
1178
+
1179
+ **文本输出格式(默认):**
1180
+
1181
+ 输出分为三个区块:主 Worktree、Worktree 列表、未清理的 Validate 快照。
1182
+
1183
+ ```
1184
+ ════════════════════════════════════════
1185
+ 项目状态总览: main-project
1186
+ ════════════════════════════════════════
1187
+
1188
+ ◆ 主 Worktree
1189
+ 分支: main
1190
+ 状态: ✓ 干净
1191
+
1192
+ ────────────────────────────────────────
1193
+
1194
+ ◆ Worktree 列表 (2 个)
1195
+
1196
+ ● feature-login [已提交]
1197
+ +120 -30 3 个本地提交 与主分支同步
1198
+ 有 validate 快照
1199
+
1200
+ ● feature-signup [未提交修改]
1201
+ +45 -10 1 个本地提交 落后主分支 2 个提交
1202
+
1203
+ ────────────────────────────────────────
1204
+
1205
+ ◆ 未清理的 Validate 快照 (1 个)
1206
+
1207
+ ⚠ old-feature (对应 worktree 已不存在)
1208
+
1209
+ ════════════════════════════════════════
1210
+ ```
1211
+
1212
+ **变更状态标签:**
1213
+
1214
+ | 状态 | 标签 | 颜色 | 说明 |
1215
+ | ----------- | -------------- | ------ | ----------------------------- |
1216
+ | `committed` | 已提交 | 绿色 | 有已提交内容,工作区干净 |
1217
+ | `uncommitted` | 未提交修改 | 黄色 | 有未提交的修改 |
1218
+ | `conflict` | 合并冲突 | 红色 | 存在合并冲突 |
1219
+ | `clean` | 无变更 | 灰色 | 工作区干净且无本地提交 |
1220
+
1221
+ **差异统计行展示规则:**
1222
+
1223
+ - 行数变更(`+N -N`)仅在有变更时展示
1224
+ - 本地提交数(`N 个本地提交`)仅在有提交时展示
1225
+ - 与主分支同步状态始终展示(落后时显示黄色,同步时显示绿色)
1226
+
1227
+ **快照区块:**
1228
+
1229
+ - 每个快照显示对应的分支名
1230
+ - 如果对应的 worktree 仍存在,显示蓝色圆点图标
1231
+ - 如果对应的 worktree 已不存在(孤立快照),显示黄色警告图标并标注 `(对应 worktree 已不存在)`
1232
+
1233
+ **JSON 输出格式(`--json`):**
1234
+
1235
+ ```json
1236
+ {
1237
+ "main": {
1238
+ "branch": "main",
1239
+ "isClean": true,
1240
+ "projectName": "main-project"
1241
+ },
1242
+ "worktrees": [
1243
+ {
1244
+ "path": "~/.clawt/worktrees/main-project/feature-login",
1245
+ "branch": "feature-login",
1246
+ "changeStatus": "committed",
1247
+ "commitsAhead": 3,
1248
+ "commitsBehind": 0,
1249
+ "hasSnapshot": true,
1250
+ "insertions": 120,
1251
+ "deletions": 30
1252
+ }
1253
+ ],
1254
+ "snapshots": [
1255
+ {
1256
+ "branch": "old-feature",
1257
+ "worktreeExists": false
1258
+ }
1259
+ ],
1260
+ "totalWorktrees": 1
1261
+ }
1262
+ ```
1263
+
1264
+ **实现要点:**
1265
+
1266
+ - 类型定义在 `src/types/status.ts`:`WorktreeDetailedStatus`、`MainWorktreeStatus`、`SnapshotInfo`、`StatusResult`
1267
+ - 消息常量在 `MESSAGES.STATUS_*` 系列
1268
+ - `getCommitCountBehind()` 是新增的工具函数(在 `src/utils/git.ts`),通过 `git rev-list --count <branch>..HEAD` 计算落后提交数
1269
+ - `getProjectSnapshotBranches()` 是新增的工具函数(在 `src/utils/validate-snapshot.ts`),通过扫描快照目录下的 `.tree` 文件提取分支名列表
1270
+
1271
+ ---
1272
+
1125
1273
  ## 6. 错误处理规范
1126
1274
 
1127
1275
  ### 6.1 通用错误处理
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawt",
3
- "version": "2.9.1",
3
+ "version": "2.10.1",
4
4
  "description": "本地并行执行多个Claude Code Agent任务,融合 Git Worktree 与 Claude Code CLI 的命令行工具",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,7 +19,17 @@ import {
19
19
  confirmAction,
20
20
  removeSnapshot,
21
21
  removeProjectSnapshots,
22
+ resolveTargetWorktrees,
22
23
  } from '../utils/index.js';
24
+ import type { WorktreeMultiResolveMessages } from '../utils/index.js';
25
+
26
+ /** remove 命令的分支解析消息配置 */
27
+ const REMOVE_RESOLVE_MESSAGES: WorktreeMultiResolveMessages = {
28
+ noWorktrees: MESSAGES.REMOVE_NO_WORKTREES,
29
+ selectBranch: MESSAGES.REMOVE_SELECT_BRANCH,
30
+ multipleMatches: MESSAGES.REMOVE_MULTIPLE_MATCHES,
31
+ noMatch: MESSAGES.REMOVE_NO_MATCH,
32
+ };
23
33
 
24
34
  /**
25
35
  * 注册 remove 命令:移除 worktree
@@ -30,38 +40,12 @@ export function registerRemoveCommand(program: Command): void {
30
40
  .command('remove')
31
41
  .description('移除 worktree(支持单个/批量/全部)')
32
42
  .option('--all', '移除当前项目下所有 worktree')
33
- .option('-b, --branch <branchName>', '指定分支名(完整分支名精确匹配)')
43
+ .option('-b, --branch <branchName>', '指定分支名(支持模糊匹配,不传则列出所有分支)')
34
44
  .action(async (options: RemoveOptions) => {
35
45
  await handleRemove(options);
36
46
  });
37
47
  }
38
48
 
39
- /**
40
- * 根据参数确定要移除的 worktree 列表
41
- * @param {RemoveOptions} options - 命令选项
42
- * @returns {Array<{path: string, branch: string}>} 待移除的 worktree 列表
43
- */
44
- function resolveWorktreesToRemove(options: RemoveOptions): Array<{ path: string; branch: string }> {
45
- const allWorktrees = getProjectWorktrees();
46
-
47
- if (options.all) {
48
- return allWorktrees;
49
- }
50
-
51
- if (!options.branch) {
52
- throw new ClawtError('请指定 --all 或 -b <branchName> 参数');
53
- }
54
-
55
- // 分支级移除:匹配 branchName 或 branchName-*
56
- const matched = allWorktrees.filter(
57
- (wt) => wt.branch === options.branch || wt.branch.startsWith(`${options.branch}-`),
58
- );
59
- if (matched.length === 0) {
60
- throw new ClawtError(MESSAGES.WORKTREE_NOT_FOUND(options.branch));
61
- }
62
- return matched;
63
- }
64
-
65
49
  /**
66
50
  * 执行 remove 命令的核心逻辑
67
51
  * @param {RemoveOptions} options - 命令选项
@@ -72,7 +56,16 @@ async function handleRemove(options: RemoveOptions): Promise<void> {
72
56
  const projectName = getProjectName();
73
57
  logger.info(`remove 命令执行,项目: ${projectName}`);
74
58
 
75
- const worktreesToRemove = resolveWorktreesToRemove(options);
59
+ const allWorktrees = getProjectWorktrees();
60
+
61
+ // 确定待移除的 worktree 列表
62
+ let worktreesToRemove;
63
+ if (options.all) {
64
+ worktreesToRemove = allWorktrees;
65
+ } else {
66
+ // 通过 fuzzy search 解析目标 worktree(精确匹配 / 模糊匹配 / 交互多选)
67
+ worktreesToRemove = await resolveTargetWorktrees(allWorktrees, REMOVE_RESOLVE_MESSAGES, options.branch);
68
+ }
76
69
 
77
70
  if (worktreesToRemove.length === 0) {
78
71
  printInfo(MESSAGES.NO_WORKTREES);