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.
- package/.claude/agent-memory/docs-sync-updater/MEMORY.md +14 -7
- package/.claude/agents/docs-sync-updater.md +11 -0
- package/README.md +80 -284
- package/dist/index.js +839 -307
- package/dist/postinstall.js +272 -0
- package/docs/spec.md +84 -22
- package/package.json +1 -1
- package/src/commands/remove.ts +21 -28
- package/src/commands/run.ts +68 -206
- package/src/constants/config.ts +4 -0
- package/src/constants/index.ts +11 -1
- package/src/constants/messages/common.ts +41 -0
- package/src/constants/messages/config.ts +5 -0
- package/src/constants/messages/create.ts +5 -0
- package/src/constants/messages/index.ts +29 -0
- package/src/constants/messages/merge.ts +42 -0
- package/src/constants/messages/remove.ts +15 -0
- package/src/constants/messages/reset.ts +7 -0
- package/src/constants/messages/resume.ts +12 -0
- package/src/constants/messages/run.ts +46 -0
- package/src/constants/messages/status.ts +25 -0
- package/src/constants/messages/sync.ts +24 -0
- package/src/constants/messages/validate.ts +25 -0
- package/src/constants/progress.ts +39 -0
- package/src/types/command.ts +4 -0
- package/src/types/config.ts +2 -0
- package/src/types/index.ts +1 -0
- package/src/types/taskFile.ts +13 -0
- package/src/utils/formatter.ts +16 -0
- package/src/utils/index.ts +8 -4
- package/src/utils/progress-render.ts +90 -0
- package/src/utils/progress.ts +213 -0
- package/src/utils/task-executor.ts +365 -0
- package/src/utils/task-file.ts +87 -0
- package/src/utils/worktree-matcher.ts +92 -0
- package/src/utils/worktree.ts +27 -0
- package/tests/unit/commands/config.test.ts +110 -0
- package/tests/unit/commands/create.test.ts +115 -0
- package/tests/unit/commands/list.test.ts +118 -0
- package/tests/unit/commands/merge.test.ts +323 -0
- package/tests/unit/commands/remove.test.ts +240 -0
- package/tests/unit/commands/reset.test.ts +124 -0
- package/tests/unit/commands/resume.test.ts +91 -0
- package/tests/unit/commands/run.test.ts +456 -0
- package/tests/unit/commands/status.test.ts +214 -0
- package/tests/unit/commands/sync.test.ts +208 -0
- package/tests/unit/commands/validate.test.ts +382 -0
- package/tests/unit/constants/config.test.ts +1 -0
- package/tests/unit/constants/messages.test.ts +1 -1
- package/tests/unit/utils/config.test.ts +21 -1
- package/tests/unit/utils/formatter.test.ts +70 -1
- package/tests/unit/utils/git.test.ts +44 -0
- package/tests/unit/utils/progress.test.ts +255 -0
- package/tests/unit/utils/task-file.test.ts +236 -0
- package/tests/unit/utils/validate-snapshot.test.ts +25 -0
- package/tests/unit/utils/worktree-matcher.test.ts +81 -5
- package/tests/unit/utils/worktree.test.ts +26 -1
package/dist/postinstall.js
CHANGED
|
@@ -10,6 +10,274 @@ 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
|
+
/** 非 TTY 环境降级输出:任务启动 */
|
|
68
|
+
PROGRESS_TASK_STARTED: (index, total, branch, path) => `[${index}/${total}] ${branch} \u542F\u52A8 ${path}`,
|
|
69
|
+
/** 非 TTY 环境降级输出:任务完成 */
|
|
70
|
+
PROGRESS_TASK_DONE: (index, total, branch, duration, cost, path) => `[${index}/${total}] ${branch} \u2713 \u5B8C\u6210 ${duration} ${cost} ${path}`,
|
|
71
|
+
/** 非 TTY 环境降级输出:任务失败 */
|
|
72
|
+
PROGRESS_TASK_FAILED: (index, total, branch, duration, path) => `[${index}/${total}] ${branch} \u2717 \u5931\u8D25 ${duration} ${path}`,
|
|
73
|
+
/** 并发限制提示 */
|
|
74
|
+
CONCURRENCY_INFO: (concurrency, total) => `\u5E76\u53D1\u9650\u5236: ${concurrency}\uFF0C\u5171 ${total} \u4E2A\u4EFB\u52A1`,
|
|
75
|
+
/** 并发数无效提示 */
|
|
76
|
+
CONCURRENCY_INVALID: "\u5E76\u53D1\u6570\u5FC5\u987B\u4E3A\u6B63\u6574\u6570",
|
|
77
|
+
/** 任务文件不存在 */
|
|
78
|
+
TASK_FILE_NOT_FOUND: (path) => `\u4EFB\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728: ${path}`,
|
|
79
|
+
/** 任务文件中没有解析到有效任务 */
|
|
80
|
+
TASK_FILE_EMPTY: "\u4EFB\u52A1\u6587\u4EF6\u4E2D\u6CA1\u6709\u89E3\u6790\u5230\u6709\u6548\u4EFB\u52A1",
|
|
81
|
+
/** 任务文件中某个任务块缺少分支名 */
|
|
82
|
+
TASK_FILE_MISSING_BRANCH: (blockIndex) => `\u4EFB\u52A1\u6587\u4EF6\u7B2C ${blockIndex} \u4E2A\u4EFB\u52A1\u5757\u7F3A\u5C11\u5206\u652F\u540D\uFF08# branch: ...\uFF09`,
|
|
83
|
+
/** 任务文件中某个任务块缺少任务描述 */
|
|
84
|
+
TASK_FILE_MISSING_TASK: (branch) => `\u4EFB\u52A1\u6587\u4EF6\u4E2D\u5206\u652F ${branch} \u7F3A\u5C11\u4EFB\u52A1\u63CF\u8FF0`,
|
|
85
|
+
/** 任务文件中某个任务块缺少任务描述(无分支名时按索引定位) */
|
|
86
|
+
TASK_FILE_MISSING_TASK_BY_INDEX: (blockIndex) => `\u4EFB\u52A1\u6587\u4EF6\u7B2C ${blockIndex} \u4E2A\u4EFB\u52A1\u5757\u7F3A\u5C11\u4EFB\u52A1\u63CF\u8FF0`,
|
|
87
|
+
/** --file 和 --tasks 不能同时使用 */
|
|
88
|
+
FILE_AND_TASKS_CONFLICT: "--file \u548C --tasks \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528",
|
|
89
|
+
/** 任务文件加载成功 */
|
|
90
|
+
TASK_FILE_LOADED: (count, path) => `\u2713 \u4ECE ${path} \u52A0\u8F7D\u4E86 ${count} \u4E2A\u4EFB\u52A1`,
|
|
91
|
+
/** 未指定 -b 或 -f */
|
|
92
|
+
BRANCH_OR_FILE_REQUIRED: "\u8BF7\u6307\u5B9A -b \u5206\u652F\u540D\u6216 -f \u4EFB\u52A1\u6587\u4EF6"
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/constants/messages/create.ts
|
|
96
|
+
var CREATE_MESSAGES = {
|
|
97
|
+
/** 创建数量参数无效 */
|
|
98
|
+
INVALID_COUNT: (value) => `\u65E0\u6548\u7684\u521B\u5EFA\u6570\u91CF: "${value}"\uFF0C\u8BF7\u8F93\u5165\u6B63\u6574\u6570`
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/constants/messages/merge.ts
|
|
102
|
+
var MERGE_MESSAGES = {
|
|
103
|
+
/** merge 成功 */
|
|
104
|
+
MERGE_SUCCESS: (branch, message, pushed) => `\u2713 \u5206\u652F ${branch} \u5DF2\u6210\u529F\u5408\u5E76\u5230\u5F53\u524D\u5206\u652F
|
|
105
|
+
\u63D0\u4EA4\u4FE1\u606F: ${message}${pushed ? "\n \u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\u4ED3\u5E93" : ""}`,
|
|
106
|
+
/** merge 成功(无提交信息,目标 worktree 已提交过) */
|
|
107
|
+
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" : ""}`,
|
|
108
|
+
/** merge 冲突 */
|
|
109
|
+
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",
|
|
110
|
+
/** merge 后清理 worktree 和分支成功 */
|
|
111
|
+
WORKTREE_CLEANED: (branch) => `\u2713 \u5DF2\u6E05\u7406 worktree \u548C\u5206\u652F: ${branch}`,
|
|
112
|
+
/** 目标 worktree 有未提交修改但未指定 -m */
|
|
113
|
+
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",
|
|
114
|
+
/** 目标 worktree 既干净又无本地提交 */
|
|
115
|
+
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",
|
|
116
|
+
/** merge 命令检测到 validate 状态的提示 */
|
|
117
|
+
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`,
|
|
118
|
+
/** merge 检测到 auto-save 提交,提示用户是否压缩 */
|
|
119
|
+
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",
|
|
120
|
+
/** squash 完成且通过 -m 直接提交后的提示 */
|
|
121
|
+
MERGE_SQUASH_COMMITTED: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u6240\u6709\u63D0\u4EA4\u538B\u7F29\u4E3A\u4E00\u4E2A`,
|
|
122
|
+
/** squash 完成但未提供 -m,提示用户自行提交 */
|
|
123
|
+
MERGE_SQUASH_PENDING: (worktreePath, branch) => `\u2713 \u5DF2\u5C06\u6240\u6709\u63D0\u4EA4\u538B\u7F29\u5230\u6682\u5B58\u533A
|
|
124
|
+
\u8BF7\u5728\u76EE\u6807 worktree \u4E2D\u63D0\u4EA4\u540E\u91CD\u65B0\u6267\u884C merge\uFF1A
|
|
125
|
+
cd ${worktreePath}
|
|
126
|
+
\u63D0\u4EA4\u5B8C\u6210\u540E\u6267\u884C\uFF1Aclawt merge -b ${branch}`,
|
|
127
|
+
/** merge 后 pull 冲突 */
|
|
128
|
+
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",
|
|
129
|
+
/** push 失败 */
|
|
130
|
+
PUSH_FAILED: "\u81EA\u52A8 push \u5931\u8D25\uFF0Cmerge \u548C pull \u5DF2\u5B8C\u6210\n \u8BF7\u624B\u52A8\u6267\u884C git push",
|
|
131
|
+
/** merge 无可用 worktree */
|
|
132
|
+
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",
|
|
133
|
+
/** merge 模糊匹配无结果,列出可用分支 */
|
|
134
|
+
MERGE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
135
|
+
\u53EF\u7528\u5206\u652F\uFF1A
|
|
136
|
+
${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
137
|
+
/** merge 交互选择提示 */
|
|
138
|
+
MERGE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u5408\u5E76\u7684\u5206\u652F",
|
|
139
|
+
/** merge 模糊匹配到多个结果提示 */
|
|
140
|
+
MERGE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/constants/messages/validate.ts
|
|
144
|
+
var VALIDATE_MESSAGES = {
|
|
145
|
+
/** validate 成功 */
|
|
146
|
+
VALIDATE_SUCCESS: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u53D8\u66F4\u5E94\u7528\u5230\u4E3B worktree
|
|
147
|
+
\u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86`,
|
|
148
|
+
/** 增量 validate 成功提示 */
|
|
149
|
+
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
|
|
150
|
+
\u6682\u5B58\u533A = \u4E0A\u6B21\u5FEB\u7167\uFF0C\u5DE5\u4F5C\u76EE\u5F55 = \u6700\u65B0\u53D8\u66F4`,
|
|
151
|
+
/** 增量 validate 降级为全量模式提示 */
|
|
152
|
+
INCREMENTAL_VALIDATE_FALLBACK: "\u589E\u91CF\u5BF9\u6BD4\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u5168\u91CF\u6A21\u5F0F",
|
|
153
|
+
/** validate 状态已清理 */
|
|
154
|
+
VALIDATE_CLEANED: (branch) => `\u2713 \u5206\u652F ${branch} \u7684 validate \u72B6\u6001\u5DF2\u6E05\u7406`,
|
|
155
|
+
/** validate patch apply 失败,提示用户同步主分支 */
|
|
156
|
+
VALIDATE_PATCH_APPLY_FAILED: (branch) => `\u53D8\u66F4\u8FC1\u79FB\u5931\u8D25\uFF1A\u76EE\u6807\u5206\u652F\u4E0E\u4E3B\u5206\u652F\u5DEE\u5F02\u8FC7\u5927
|
|
157
|
+
\u8BF7\u5148\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`,
|
|
158
|
+
/** validate 无可用 worktree */
|
|
159
|
+
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",
|
|
160
|
+
/** validate 模糊匹配无结果,列出可用分支 */
|
|
161
|
+
VALIDATE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
162
|
+
\u53EF\u7528\u5206\u652F\uFF1A
|
|
163
|
+
${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
164
|
+
/** validate 交互选择提示 */
|
|
165
|
+
VALIDATE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u9A8C\u8BC1\u7684\u5206\u652F",
|
|
166
|
+
/** validate 模糊匹配到多个结果提示 */
|
|
167
|
+
VALIDATE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/constants/messages/sync.ts
|
|
171
|
+
var SYNC_MESSAGES = {
|
|
172
|
+
/** sync 自动保存未提交变更 */
|
|
173
|
+
SYNC_AUTO_COMMITTED: (branch) => `\u5DF2\u81EA\u52A8\u4FDD\u5B58 ${branch} \u5206\u652F\u7684\u672A\u63D0\u4EA4\u53D8\u66F4`,
|
|
174
|
+
/** sync 开始合并 */
|
|
175
|
+
SYNC_MERGING: (targetBranch, mainBranch) => `\u6B63\u5728\u5C06 ${mainBranch} \u5408\u5E76\u5230 ${targetBranch} ...`,
|
|
176
|
+
/** sync 成功 */
|
|
177
|
+
SYNC_SUCCESS: (targetBranch, mainBranch) => `\u2713 \u5DF2\u5C06 ${mainBranch} \u7684\u6700\u65B0\u4EE3\u7801\u540C\u6B65\u5230 ${targetBranch}`,
|
|
178
|
+
/** sync 冲突 */
|
|
179
|
+
SYNC_CONFLICT: (worktreePath) => `\u5408\u5E76\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u8FDB\u5165\u76EE\u6807 worktree \u624B\u52A8\u89E3\u51B3\uFF1A
|
|
180
|
+
cd ${worktreePath}
|
|
181
|
+
\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue
|
|
182
|
+
clawt validate -b <branch> \u9A8C\u8BC1\u53D8\u66F4`,
|
|
183
|
+
/** sync 无可用 worktree */
|
|
184
|
+
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",
|
|
185
|
+
/** sync 模糊匹配无结果,列出可用分支 */
|
|
186
|
+
SYNC_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
187
|
+
\u53EF\u7528\u5206\u652F\uFF1A
|
|
188
|
+
${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
189
|
+
/** sync 交互选择提示 */
|
|
190
|
+
SYNC_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u540C\u6B65\u7684\u5206\u652F",
|
|
191
|
+
/** sync 模糊匹配到多个结果提示 */
|
|
192
|
+
SYNC_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// src/constants/messages/resume.ts
|
|
196
|
+
var RESUME_MESSAGES = {
|
|
197
|
+
/** resume 无可用 worktree */
|
|
198
|
+
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",
|
|
199
|
+
/** resume 模糊匹配无结果,列出可用分支 */
|
|
200
|
+
RESUME_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
201
|
+
\u53EF\u7528\u5206\u652F\uFF1A
|
|
202
|
+
${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
203
|
+
/** resume 交互选择提示 */
|
|
204
|
+
RESUME_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u6062\u590D\u7684\u5206\u652F",
|
|
205
|
+
/** resume 模糊匹配到多个结果提示 */
|
|
206
|
+
RESUME_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// src/constants/messages/remove.ts
|
|
210
|
+
var REMOVE_MESSAGES = {
|
|
211
|
+
/** remove 无可用 worktree */
|
|
212
|
+
REMOVE_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u65E0\u9700\u79FB\u9664",
|
|
213
|
+
/** remove 多选交互提示 */
|
|
214
|
+
REMOVE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u79FB\u9664\u7684\u5206\u652F\uFF08\u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
|
|
215
|
+
/** remove 模糊匹配到多个结果提示 */
|
|
216
|
+
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`,
|
|
217
|
+
/** remove 模糊匹配无结果,列出可用分支 */
|
|
218
|
+
REMOVE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
219
|
+
\u53EF\u7528\u5206\u652F\uFF1A
|
|
220
|
+
${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
221
|
+
/** 批量移除部分失败 */
|
|
222
|
+
REMOVE_PARTIAL_FAILURE: (failures) => `\u4EE5\u4E0B worktree \u79FB\u9664\u5931\u8D25\uFF1A
|
|
223
|
+
${failures.map((f) => ` \u2717 ${f.path}: ${f.error}`).join("\n")}`
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// src/constants/messages/reset.ts
|
|
227
|
+
var RESET_MESSAGES = {
|
|
228
|
+
/** reset 成功 */
|
|
229
|
+
RESET_SUCCESS: "\u2713 \u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u91CD\u7F6E",
|
|
230
|
+
/** reset 时工作区和暂存区已干净 */
|
|
231
|
+
RESET_ALREADY_CLEAN: "\u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u662F\u5E72\u51C0\u72B6\u6001\uFF0C\u65E0\u9700\u91CD\u7F6E"
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// src/constants/messages/config.ts
|
|
235
|
+
var CONFIG_CMD_MESSAGES = {
|
|
236
|
+
/** 配置已恢复为默认值 */
|
|
237
|
+
CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C"
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// src/constants/messages/status.ts
|
|
241
|
+
var STATUS_MESSAGES = {
|
|
242
|
+
/** status 命令标题 */
|
|
243
|
+
STATUS_TITLE: (projectName) => `\u9879\u76EE\u72B6\u6001\u603B\u89C8: ${projectName}`,
|
|
244
|
+
/** status 主 worktree 区块标题 */
|
|
245
|
+
STATUS_MAIN_SECTION: "\u4E3B Worktree",
|
|
246
|
+
/** status worktrees 区块标题 */
|
|
247
|
+
STATUS_WORKTREES_SECTION: "Worktree \u5217\u8868",
|
|
248
|
+
/** status 快照区块标题 */
|
|
249
|
+
STATUS_SNAPSHOTS_SECTION: "\u672A\u6E05\u7406\u7684 Validate \u5FEB\u7167",
|
|
250
|
+
/** status 无 worktree */
|
|
251
|
+
STATUS_NO_WORKTREES: "(\u65E0\u6D3B\u8DC3 worktree)",
|
|
252
|
+
/** status 无未清理快照 */
|
|
253
|
+
STATUS_NO_SNAPSHOTS: "(\u65E0\u672A\u6E05\u7406\u7684\u5FEB\u7167)",
|
|
254
|
+
/** status 变更状态:已提交 */
|
|
255
|
+
STATUS_CHANGE_COMMITTED: "\u5DF2\u63D0\u4EA4",
|
|
256
|
+
/** status 变更状态:未提交修改 */
|
|
257
|
+
STATUS_CHANGE_UNCOMMITTED: "\u672A\u63D0\u4EA4\u4FEE\u6539",
|
|
258
|
+
/** status 变更状态:合并冲突 */
|
|
259
|
+
STATUS_CHANGE_CONFLICT: "\u5408\u5E76\u51B2\u7A81",
|
|
260
|
+
/** status 变更状态:无变更 */
|
|
261
|
+
STATUS_CHANGE_CLEAN: "\u65E0\u53D8\u66F4",
|
|
262
|
+
/** status 快照对应 worktree 已不存在 */
|
|
263
|
+
STATUS_SNAPSHOT_ORPHANED: "(\u5BF9\u5E94 worktree \u5DF2\u4E0D\u5B58\u5728)"
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// src/constants/messages/index.ts
|
|
267
|
+
var MESSAGES = {
|
|
268
|
+
...COMMON_MESSAGES,
|
|
269
|
+
...RUN_MESSAGES,
|
|
270
|
+
...CREATE_MESSAGES,
|
|
271
|
+
...MERGE_MESSAGES,
|
|
272
|
+
...VALIDATE_MESSAGES,
|
|
273
|
+
...SYNC_MESSAGES,
|
|
274
|
+
...RESUME_MESSAGES,
|
|
275
|
+
...REMOVE_MESSAGES,
|
|
276
|
+
...RESET_MESSAGES,
|
|
277
|
+
...CONFIG_CMD_MESSAGES,
|
|
278
|
+
...STATUS_MESSAGES
|
|
279
|
+
};
|
|
280
|
+
|
|
13
281
|
// src/constants/config.ts
|
|
14
282
|
var CONFIG_DEFINITIONS = {
|
|
15
283
|
autoDeleteBranch: {
|
|
@@ -27,6 +295,10 @@ var CONFIG_DEFINITIONS = {
|
|
|
27
295
|
confirmDestructiveOps: {
|
|
28
296
|
defaultValue: true,
|
|
29
297
|
description: "\u6267\u884C\u7834\u574F\u6027\u64CD\u4F5C\uFF08reset\u3001validate --clean\uFF09\u524D\u662F\u5426\u63D0\u793A\u786E\u8BA4"
|
|
298
|
+
},
|
|
299
|
+
maxConcurrency: {
|
|
300
|
+
defaultValue: 0,
|
|
301
|
+
description: "run \u547D\u4EE4\u9ED8\u8BA4\u6700\u5927\u5E76\u53D1\u6570\uFF0C0 \u8868\u793A\u4E0D\u9650\u5236"
|
|
30
302
|
}
|
|
31
303
|
};
|
|
32
304
|
function deriveDefaultConfig(definitions) {
|
package/docs/spec.md
CHANGED
|
@@ -168,10 +168,10 @@ git show-ref --verify refs/heads/<branchName> 2>/dev/null
|
|
|
168
168
|
| 命令 | 说明 | 对应场景 |
|
|
169
169
|
| --------------------- | ---------------------------------------------- | -------- |
|
|
170
170
|
| `clawt create` | 批量创建 worktree 及对应分支 | 5.1 |
|
|
171
|
-
| `clawt run` | 批量创建 worktree + 启动 Claude Code
|
|
171
|
+
| `clawt run` | 批量创建 worktree + 启动 Claude Code 执行任务(支持任务文件) | 5.2 |
|
|
172
172
|
| `clawt validate` | 在主 worktree 验证某个 worktree 分支的变更 | 5.4 |
|
|
173
173
|
| `clawt merge` | 合并某个已验证的 worktree 分支到主 worktree | 5.6 |
|
|
174
|
-
| `clawt remove` | 移除 worktree
|
|
174
|
+
| `clawt remove` | 移除 worktree(支持模糊匹配/多选/全部) | 5.5 |
|
|
175
175
|
| `clawt list` | 列出当前项目所有 worktree(支持 `--json` 格式输出) | 5.8 |
|
|
176
176
|
| `clawt config` | 查看全局配置 | 5.10 |
|
|
177
177
|
| `clawt config reset` | 将配置恢复为默认值 | 5.10 |
|
|
@@ -259,7 +259,10 @@ clawt create -b <branchName> [-n <count>]
|
|
|
259
259
|
# 方式一:通过 --tasks 参数直接指定任务(多任务并行)
|
|
260
260
|
clawt run -b <branchName> --tasks <task1> --tasks <task2> --tasks <task3>
|
|
261
261
|
|
|
262
|
-
#
|
|
262
|
+
# 方式二:通过 -f 从任务文件读取任务列表
|
|
263
|
+
clawt run -f <path>
|
|
264
|
+
|
|
265
|
+
# 方式三:不传 --tasks 也不传 -f,在 worktree 中打开 Claude Code 交互式界面
|
|
263
266
|
clawt run -b <branchName>
|
|
264
267
|
```
|
|
265
268
|
|
|
@@ -267,21 +270,67 @@ clawt run -b <branchName>
|
|
|
267
270
|
|
|
268
271
|
| 参数 | 必填 | 说明 |
|
|
269
272
|
| --------- | ---- | ----------------------------------------------------------- |
|
|
270
|
-
| `-b` |
|
|
273
|
+
| `-b` | 否 | 分支名(使用 `-f` 时可选,否则必填) |
|
|
271
274
|
| `--tasks` | 否 | 任务描述(可多次指定,每个 --tasks 对应一个任务,任务数量即 worktree 数量)。不传则在 worktree 中打开 Claude Code 交互式界面 |
|
|
275
|
+
| `-f` | 否 | 从任务文件读取任务列表(与 `--tasks` 互斥) |
|
|
276
|
+
| `-c` | 否 | 最大并发数,`0` 表示不限制 |
|
|
277
|
+
|
|
278
|
+
**互斥约束:**
|
|
279
|
+
|
|
280
|
+
- `--file` 和 `--tasks` **不能同时使用**
|
|
281
|
+
- 非 `-f` 模式必须指定 `-b`
|
|
272
282
|
|
|
273
283
|
**交互式 Claude Code 界面模式:**
|
|
274
284
|
|
|
275
|
-
当不传 `--tasks` 时,会创建单个 worktree,然后通过 `spawnSync` + `inherit stdio` 在该 worktree 中直接启动 Claude Code CLI 交互式界面,让用户与 Claude Code 直接交互。
|
|
285
|
+
当不传 `--tasks` 也不传 `-f` 时,会创建单个 worktree,然后通过 `spawnSync` + `inherit stdio` 在该 worktree 中直接启动 Claude Code CLI 交互式界面,让用户与 Claude Code 直接交互。
|
|
276
286
|
|
|
277
287
|
启动命令通过配置项 `claudeCodeCommand`(默认值 `claude`)指定,支持自定义命令及参数。
|
|
278
288
|
|
|
279
|
-
|
|
289
|
+
#### 任务文件格式
|
|
290
|
+
|
|
291
|
+
任务文件使用 Markdown 文件中嵌入 HTML 注释标签的自定义格式,标签外的任何文本都不会被解析。
|
|
292
|
+
|
|
293
|
+
```markdown
|
|
294
|
+
这里可以写任何说明文字,会被忽略
|
|
295
|
+
|
|
296
|
+
<!-- CLAWT-TASKS:START -->
|
|
297
|
+
# branch: feat-login
|
|
298
|
+
实现用户登录功能
|
|
299
|
+
<!-- CLAWT-TASKS:END -->
|
|
300
|
+
|
|
301
|
+
<!-- CLAWT-TASKS:START -->
|
|
302
|
+
# branch: fix-bug
|
|
303
|
+
修复内存泄漏问题
|
|
304
|
+
这是多行任务描述
|
|
305
|
+
可以写很多行
|
|
306
|
+
<!-- CLAWT-TASKS:END -->
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**格式规则:**
|
|
310
|
+
|
|
311
|
+
1. **任务块界定**:每个任务用 `<!-- CLAWT-TASKS:START -->` 和 `<!-- CLAWT-TASKS:END -->` 包裹
|
|
312
|
+
2. **分支名声明**:块内必须有一行 `# branch: <分支名>`(冒号前后的空格可灵活)
|
|
313
|
+
3. **任务描述**:块内除分支名行以外的所有行,合并为任务描述(支持多行)
|
|
314
|
+
4. **块外内容忽略**:标签外的任何文本都不会被解析
|
|
315
|
+
5. **必填校验**:每个块必须包含任务描述;分支名默认必填,但使用 `-b` 参数时分支名为可选(会被忽略,用 `-b` 值自动编号)
|
|
316
|
+
|
|
317
|
+
**解析实现:** `src/utils/task-file.ts` 中的 `parseTaskFile()` 和 `loadTaskFile()` 函数,类型定义为 `TaskFileEntry`(`src/types/taskFile.ts`)。
|
|
318
|
+
|
|
319
|
+
#### 任务文件模式运行流程
|
|
320
|
+
|
|
321
|
+
使用 `-f` 时的执行路径(`handleRun` → `handleRunFromFile`):
|
|
322
|
+
|
|
323
|
+
1. 调用 `loadTaskFile(options.file)` 读取解析文件
|
|
324
|
+
2. **有 `-b` 参数**:忽略文件中的分支名,用 `-b` 值自动编号创建 worktree(`createWorktrees(branch, count)`)
|
|
325
|
+
3. **无 `-b` 参数**:使用文件中每个任务的独立分支名,先 `sanitizeBranchName` 清理后调用 `createWorktreesByBranches(branches)`
|
|
326
|
+
4. 调用 `executeBatchTasks(worktrees, tasks, concurrency)` 执行
|
|
327
|
+
|
|
328
|
+
#### --tasks 模式运行流程
|
|
280
329
|
|
|
281
330
|
1. 若传了 `--tasks`,解析得到任务数组 `tasks[]`;若未传,先检测分支是否已存在(已存在则提示使用 `clawt resume -b <branchName>` 恢复会话),然后创建单个 worktree 并启动 Claude Code 交互式界面(流程结束,不进入后续并行执行阶段)
|
|
282
331
|
2. `n = tasks.length`
|
|
283
332
|
3. 按照 **5.1** 的流程创建 `n` 个 worktree
|
|
284
|
-
4.
|
|
333
|
+
4. 通过公共函数 `executeBatchTasks`(`src/utils/task-executor.ts`)启动批量任务执行,该函数负责进度面板渲染、SIGINT 中断处理、并发控制和汇总输出。对每个 worktree 并行启动 Claude Code CLI:
|
|
285
334
|
```bash
|
|
286
335
|
cd ~/.clawt/worktrees/<project>/<branchName>-<i>
|
|
287
336
|
claude -p "<tasks[i]>" --output-format json --permission-mode bypassPermissions
|
|
@@ -329,7 +378,7 @@ Claude Code CLI 以 `--output-format json` 运行时,退出后会在 stdout
|
|
|
329
378
|
1. 为每个 Claude Code 子进程维护状态(运行中 / 已完成 / 已失败)
|
|
330
379
|
2. 监听每个子进程的 `close` 事件(基于 Node.js `ChildProcess` 的事件驱动机制)
|
|
331
380
|
3. 当某个子进程触发 `close` 事件时,解析其 stdout 输出的 JSON
|
|
332
|
-
4. 在主 worktree 的 clawt
|
|
381
|
+
4. 在主 worktree 的 clawt 终端实时输出通知(进度面板每个任务行末尾显示 worktree 路径,终端可点击跳转):
|
|
333
382
|
|
|
334
383
|
```
|
|
335
384
|
✓ [完成] worktree: ~/.clawt/worktrees/main-project/feature-scheme-1
|
|
@@ -574,29 +623,42 @@ git apply --cached < patch
|
|
|
574
623
|
**命令:**
|
|
575
624
|
|
|
576
625
|
```bash
|
|
577
|
-
|
|
626
|
+
# 移除当前项目所有 worktree
|
|
627
|
+
clawt remove --all
|
|
628
|
+
|
|
629
|
+
# 指定分支名(支持模糊匹配)
|
|
630
|
+
clawt remove -b <branchName>
|
|
631
|
+
|
|
632
|
+
# 不指定参数(列出所有分支供多选)
|
|
633
|
+
clawt remove
|
|
578
634
|
```
|
|
579
635
|
|
|
580
636
|
**参数:**
|
|
581
637
|
|
|
582
|
-
| 参数 | 说明
|
|
583
|
-
| --------- |
|
|
584
|
-
| `--all` | 移除当前项目 (`~/.clawt/worktrees/<project>/`) 下所有 worktree
|
|
585
|
-
| `-b
|
|
586
|
-
|
|
587
|
-
**三种移除粒度:**
|
|
638
|
+
| 参数 | 必填 | 说明 |
|
|
639
|
+
| --------- | ---- | ---------------------------------------------------------------------- |
|
|
640
|
+
| `--all` | 否 | 移除当前项目 (`~/.clawt/worktrees/<project>/`) 下所有 worktree |
|
|
641
|
+
| `-b` | 否 | 指定分支名(支持模糊匹配,不传则列出所有分支供多选) |
|
|
588
642
|
|
|
589
|
-
|
|
590
|
-
| ---- | ---------------------------------------- | ------------------------------------------------------------- |
|
|
591
|
-
| 全部 | `clawt remove --all` | `~/.clawt/worktrees/<project>/` 下所有 worktree |
|
|
592
|
-
| 分支 | `clawt remove -b feature-scheme` | `~/.clawt/worktrees/<project>/feature-scheme-*` 的所有 worktree |
|
|
593
|
-
| 单个 | `clawt remove -b feature-scheme-2` | 仅移除 `feature-scheme-2` 对应的 worktree(完整分支名精确匹配) |
|
|
643
|
+
> **提示:** 不传 `--all` 也不传 `-b` 时,会列出当前项目所有 worktree 供交互式多选。
|
|
594
644
|
|
|
595
645
|
**运行流程:**
|
|
596
646
|
|
|
597
647
|
1. **主 worktree 校验** (2.1)
|
|
598
648
|
2. **获取项目名** (2.2)
|
|
599
|
-
3.
|
|
649
|
+
3. **确定待移除的 worktree 列表**:
|
|
650
|
+
- **指定 `--all`** → 选中当前项目所有 worktree
|
|
651
|
+
- **未指定 `--all`** → 通过 `resolveTargetWorktrees` 解析目标 worktree(多选版本),匹配策略如下:
|
|
652
|
+
- **未传 `-b` 参数**:
|
|
653
|
+
- 无可用 worktree → 报错退出
|
|
654
|
+
- 仅 1 个 worktree → 直接使用,无需选择
|
|
655
|
+
- 多个 worktree → 通过交互式多选列表(Enquirer.MultiSelect)让用户选择(空格选择,回车确认)
|
|
656
|
+
- **传了 `-b` 参数**:
|
|
657
|
+
1. **精确匹配优先**:在 worktree 列表中查找分支名完全相同的 worktree,找到则直接使用
|
|
658
|
+
2. **模糊匹配**(子串匹配,大小写不敏感):
|
|
659
|
+
- 唯一匹配 → 直接使用
|
|
660
|
+
- 多个匹配 → 通过交互式多选列表让用户从匹配结果中选择
|
|
661
|
+
3. **无匹配** → 报错退出,并列出所有可用分支名
|
|
600
662
|
4. 列出即将移除的 worktree 及对应分支:
|
|
601
663
|
|
|
602
664
|
```
|
|
@@ -1303,7 +1365,7 @@ clawt status [--json]
|
|
|
1303
1365
|
- 测试辅助文件:
|
|
1304
1366
|
- `tests/helpers/setup.ts`:全局 setup,禁用 chalk 颜色输出避免 ANSI 转义码干扰断言
|
|
1305
1367
|
- `tests/helpers/fixtures.ts`:测试数据工厂,提供 `createWorktreeInfo()`、`createWorktreeStatus()`、`createWorktreeList()` 等工厂函数
|
|
1306
|
-
- 覆盖范围:`src/` 下的 `utils/`、`errors/`、`constants/`
|
|
1368
|
+
- 覆盖范围:`src/` 下的 `commands/`、`utils/`、`errors/`、`constants/` 全部关键模块
|
|
1307
1369
|
- 覆盖率统计排除项:`src/index.ts`(入口文件)、`src/types/**`(类型定义)、`src/logger/**`(日志模块)
|
|
1308
1370
|
- npm 脚本:
|
|
1309
1371
|
- `npm test`:执行全部测试(`vitest run`)
|
package/package.json
CHANGED
package/src/commands/remove.ts
CHANGED
|
@@ -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
|
|
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);
|