clawt 3.7.0 → 3.7.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.
package/docs/status.md ADDED
@@ -0,0 +1,345 @@
1
+ ### 5.14 项目全局状态总览
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ clawt status [--json] [-i | --interactive]
7
+ ```
8
+
9
+ **参数:**
10
+
11
+ | 参数 | 必填 | 说明 |
12
+ | --------------------- | ---- | ---------------------------------------- |
13
+ | `--json` | 否 | 以 JSON 格式输出完整状态数据 |
14
+ | `-i, --interactive` | 否 | 启动交互式面板模式(TUI 实时刷新面板) |
15
+
16
+ **使用场景:**
17
+
18
+ 在管理多个 worktree 时,快速了解项目全局状态:主 worktree 当前分支及干净状态、配置的主工作分支信息、所有 worktree 的变更情况和与主分支的同步状态、validate 快照摘要。
19
+
20
+ 交互式面板模式适用于需要持续监控项目状态并快速执行操作的场景,无需反复输入命令。
21
+
22
+ **运行流程:**
23
+
24
+ 1. **主 worktree 校验** (2.1)
25
+ 2. **收集主 worktree 状态**:
26
+ - 获取当前分支名(`getCurrentBranch()`)
27
+ - 检测工作区是否干净(`isWorkingDirClean()`)
28
+ - 获取项目名(`getProjectName()`)
29
+ - 加载项目配置(`loadProjectConfig()`),读取配置的主工作分支名(`clawtMainWorkBranch`)
30
+ - 检测配置的主工作分支是否存在(`checkBranchExists()`)
31
+ 3. **收集各 worktree 详细状态**:
32
+ - 获取项目所有 worktree(`getProjectWorktrees()`)
33
+ - 对每个 worktree 收集以下信息:
34
+ - **变更状态**(优先级:合并冲突 > 未提交修改 > 已提交 > 无变更)
35
+ - **行数差异**(新增/删除行数,通过 `getDiffStat()` 获取)
36
+ - **提交差异**(相对于主分支的领先提交数 `getCommitCountAhead()` 和落后提交数 `getCommitCountBehind()`)
37
+ - **快照时间**(validate 快照文件的 mtime,通过 `getSnapshotModifiedTime()` 获取,返回 ISO 8601 时间字符串或 null)
38
+ - **创建时间**(通过 `getWorktreeCreatedTime()` 从文件系统 birthtime 获取 worktree 目录的创建时间)
39
+ 4. **收集 validate 快照摘要**:
40
+ - 通过 `getProjectSnapshotBranches()` 扫描快照目录下的 `.tree` 文件获取所有存在快照的分支名
41
+ - 统计快照总数和孤立快照数(对应 worktree 已不存在的快照)
42
+ 5. **输出状态信息**:
43
+ - 指定 `--json` → 以 JSON 格式输出完整状态数据(`JSON.stringify`)
44
+ - 指定 `-i` / `--interactive` → 启动交互式面板模式(见下方「交互式面板模式」章节)
45
+ - 未指定 → 以文本格式输出
46
+
47
+ **文本输出格式(默认):**
48
+
49
+ 输出分为三个区块:主 Worktree、Worktree 列表、Validate 快照摘要。每个 worktree 条目每行展示一种信息。
50
+
51
+ 主 Worktree 区块会显示配置的主工作分支信息,根据状态有以下三种展示:
52
+ - **正常**(灰色):`主工作分支: <branchName>`
53
+ - **当前分支不一致**(黄色):`⚠ 主工作分支: <branchName>(当前分支不一致,如需更新请执行 clawt init)`
54
+ - **分支已不存在**(红色):`✗ 主工作分支: <branchName>(已不存在,请执行 clawt init 重新设置)`
55
+
56
+ 注意:当项目未初始化(`configuredMainBranch` 为 null)时不展示配置分支信息;当主 worktree 当前处于验证分支(`VALIDATE_BRANCH_PREFIX` 前缀)时不显示不一致警告。
57
+
58
+ ```
59
+ ════════════════════════════════════════
60
+ 项目状态总览: main-project
61
+ ════════════════════════════════════════
62
+
63
+ ◆ 主 Worktree
64
+ 分支: main
65
+ 状态: ✓ 干净
66
+ 主工作分支: main
67
+
68
+ ────────────────────────────────────────
69
+
70
+ ◆ Worktree 列表 (2 个)
71
+
72
+ ● feature-login [已提交]
73
+ +120 -30
74
+ 3 个本地提交
75
+ 与主分支同步
76
+ 创建于 3 天前
77
+ 上次验证: 2 小时前
78
+
79
+ ● feature-signup [未提交修改]
80
+ +45 -10
81
+ 1 个本地提交
82
+ 落后主分支 2 个提交
83
+ 创建于 1 天前
84
+ ✗ 未验证
85
+
86
+ ────────────────────────────────────────
87
+
88
+ ◆ Validate 快照 (3 个)
89
+ 其中 1 个快照对应的 worktree 已不存在
90
+
91
+ ════════════════════════════════════════
92
+ ```
93
+
94
+ **变更状态标签:**
95
+
96
+ | 状态 | 标签 | 颜色 | 说明 |
97
+ | ----------- | -------------- | ------ | ----------------------------- |
98
+ | `committed` | 已提交 | 绿色 | 有已提交内容,工作区干净 |
99
+ | `uncommitted` | 未提交修改 | 黄色 | 有未提交的修改 |
100
+ | `conflict` | 合并冲突 | 红色 | 存在合并冲突 |
101
+ | `clean` | 无变更 | 灰色 | 工作区干净且无本地提交 |
102
+
103
+ **差异统计展示规则(每项独立一行):**
104
+
105
+ - 行数变更(`+N -N`):仅在有变更时展示,独立一行
106
+ - 本地提交数(`N 个本地提交`):仅在有提交时展示,独立一行(黄色)
107
+ - 与主分支同步状态:始终展示,独立一行(落后时显示黄色,同步时显示绿色)
108
+
109
+ **创建时间行:**
110
+
111
+ - 通过 `getWorktreeCreatedTime()` 从文件系统 `birthtime` 获取 worktree 目录的创建时间,以 `formatRelativeTime()` 格式化为中文相对时间(如"3 天前"、"2 小时前"、"刚刚")
112
+ - 展示为灰色文本 `创建于 X前`,无法获取时不展示
113
+
114
+ **验证状态行:**
115
+
116
+ - 有快照时:显示绿色 `上次验证: X前`(通过 `getSnapshotModifiedTime()` 获取快照文件 mtime,再用 `formatRelativeTime()` 格式化)
117
+ - 无快照时:显示红色 `✗ 未验证` 警示
118
+
119
+ **快照区块:**
120
+
121
+ - 标题显示快照总数
122
+ - 如果存在孤立快照(对应 worktree 已不存在),显示黄色警告 `其中 N 个快照对应的 worktree 已不存在`
123
+ - 无孤立快照时不显示额外信息
124
+
125
+ **JSON 输出格式(`--json`):**
126
+
127
+ ```json
128
+ {
129
+ "main": {
130
+ "branch": "main",
131
+ "isClean": true,
132
+ "projectName": "main-project",
133
+ "configuredMainBranch": "main",
134
+ "configuredBranchExists": true
135
+ },
136
+ "worktrees": [
137
+ {
138
+ "path": "~/.clawt/worktrees/main-project/feature-login",
139
+ "branch": "feature-login",
140
+ "changeStatus": "committed",
141
+ "commitsAhead": 3,
142
+ "commitsBehind": 0,
143
+ "snapshotTime": "2025-02-06T12:30:00.000Z",
144
+ "insertions": 120,
145
+ "deletions": 30,
146
+ "createdAt": "2025-02-03T10:00:00.000Z"
147
+ }
148
+ ],
149
+ "snapshots": {
150
+ "total": 3,
151
+ "orphaned": 1
152
+ },
153
+ "totalWorktrees": 1
154
+ }
155
+ ```
156
+
157
+ `MainWorktreeStatus` 各字段说明:
158
+
159
+ | 字段 | 类型 | 说明 |
160
+ | ----------------------- | ----------------- | ------------------------------------------- |
161
+ | `branch` | `string` | 当前分支名 |
162
+ | `isClean` | `boolean` | 工作区是否干净 |
163
+ | `projectName` | `string` | 项目名 |
164
+ | `configuredMainBranch` | `string \| null` | 配置的主工作分支名(项目未初始化时为 null) |
165
+ | `configuredBranchExists`| `boolean \| null` | 配置的主工作分支是否存在(项目未初始化时为 null)|
166
+
167
+ **实现要点:**
168
+
169
+ - 类型定义在 `src/types/status.ts`:`WorktreeDetailedStatus`(`snapshotTime: string | null`、`createdAt: string | null`)、`MainWorktreeStatus`(包含 `configuredMainBranch` 和 `configuredBranchExists`)、`SnapshotInfo`、`SnapshotSummary`(包含 `total` 和 `orphaned`)、`StatusResult`(`snapshots` 为 `SnapshotSummary` 类型)
170
+ - 消息常量在 `MESSAGES.STATUS_*` 系列:
171
+ - `STATUS_TITLE(projectName)`:标题文本
172
+ - `STATUS_MAIN_SECTION`:主 worktree 区块标题
173
+ - `STATUS_WORKTREES_SECTION`:worktree 列表区块标题
174
+ - `STATUS_SNAPSHOTS_SECTION`:快照区块标题
175
+ - `STATUS_NO_WORKTREES`:无活跃 worktree 提示
176
+ - `STATUS_CHANGE_COMMITTED` / `STATUS_CHANGE_UNCOMMITTED` / `STATUS_CHANGE_CONFLICT` / `STATUS_CHANGE_CLEAN`:变更状态标签
177
+ - `STATUS_LAST_VALIDATED(relativeTime)`:上次验证时间标签
178
+ - `STATUS_NOT_VALIDATED`:未验证红色警示文本(`✗ 未验证`)
179
+ - `STATUS_CREATED_AT(relativeTime)`:创建时间标签
180
+ - `STATUS_SNAPSHOT_ORPHANED(count)`:孤立快照警告(接受数量参数)
181
+ - `STATUS_CONFIGURED_BRANCH(branchName)`:配置的主工作分支(正常状态,灰色)
182
+ - `STATUS_CONFIGURED_BRANCH_DELETED(branchName)`:配置的主工作分支已不存在(红色)
183
+ - `STATUS_CONFIGURED_BRANCH_MISMATCH(branchName)`:当前分支与配置不一致(黄色)
184
+ - `getWorktreeCreatedTime()` 工具函数(在 `src/utils/worktree-matcher.ts`),通过 `fs.statSync().birthtime` 获取 worktree 目录的创建时间,返回 ISO 8601 格式字符串或 null
185
+ - `getSnapshotModifiedTime()` 工具函数(在 `src/utils/validate-snapshot.ts`),通过 `fs.statSync` 获取快照文件的修改时间(mtime),返回 UTC 时区的 ISO 8601 格式字符串(`toISOString()` 格式)或 null
186
+ - `formatRelativeTime()` 格式化函数(在 `src/utils/formatter.ts`),将 ISO 8601 日期字符串转换为中文相对时间描述(如"3 天前"、"2 小时前"、"刚刚"),无效日期时返回 null
187
+ - `getCommitCountBehind()` 工具函数(在 `src/utils/git-branch.ts`),通过 `git rev-list --count <branch>..HEAD` 计算落后提交数
188
+ - `getProjectSnapshotBranches()` 工具函数(在 `src/utils/validate-snapshot.ts`),通过扫描快照目录下的 `.tree` 文件提取分支名列表
189
+ - `loadProjectConfig()` 加载项目配置,读取 `clawtMainWorkBranch` 字段
190
+ - `checkBranchExists()` 检测分支是否存在
191
+
192
+ **交互式面板模式(`-i` / `--interactive`):**
193
+
194
+ 通过 `-i` 选项启动一个实时刷新的 TUI(Text User Interface)面板,在终端中提供键盘导航和快捷键操作功能。面板基于备选屏幕(alternate screen)渲染,退出后终端恢复原状。
195
+
196
+ **前置条件:**
197
+
198
+ - 需要 TTY 终端环境。非 TTY 时(如管道、重定向场景)会输出降级提示并退出。
199
+
200
+ **面板布局:**
201
+
202
+ ```
203
+ 项目状态总览: my-project
204
+ 主工作分支: main
205
+ 快照: 3 个(1 个孤立)
206
+ ──────── ↑ 更多 worktree... ────────
207
+ ════ 2026-03-01(2 天前) ════
208
+
209
+ ▶ feature-login [已提交]
210
+ +120 -30
211
+ 3 个本地提交
212
+ 与主分支同步
213
+ 创建于 3 天前
214
+ 上次验证: 2 小时前
215
+
216
+ feature-signup [未提交修改]
217
+ +45 -10
218
+ 1 个本地提交
219
+ 落后主分支 2 个提交
220
+ 创建于 1 天前
221
+ ✗ 未验证
222
+
223
+ ════ 2026-02-28(3 天前) ════
224
+
225
+ fix-bug [无变更]
226
+ 与主分支同步
227
+ 创建于 5 天前
228
+ ✗ 未验证
229
+
230
+ ──────── ↓ 更多 worktree... ────────
231
+ [v]验证 [m]合并 [d]删除 [r]恢复 [s]同步 [c]覆盖 [f]刷新 [q]退出 (3s 后刷新)
232
+ ```
233
+
234
+ 面板从上到下分为以下区域:
235
+
236
+ 1. **标题行**:显示项目名(`项目状态总览: <projectName>`)
237
+ 2. **配置分支信息行**:显示配置的主工作分支状态,有以下四种情况:
238
+ - 正常(灰色):`主工作分支: <branchName>`
239
+ - 分支已删除(红色):`✗ 主工作分支: <branchName>(已不存在)`
240
+ - 分支不一致(黄色):`⚠ 主工作分支: <branchName>(不一致)`
241
+ - 未初始化(灰色):`未初始化(执行 clawt init 设置主工作分支)`
242
+ 3. **快照摘要行**:显示快照总数和孤立快照数
243
+ 4. **顶部分隔线**:当存在向上溢出时,分隔线中间嵌入 `↑ 更多 worktree...` 提示
244
+ 5. **Worktree 滚动区域**:按日期分组显示 worktree 列表,支持上下滚动
245
+ 6. **底部分隔线**:当存在向下溢出时,分隔线中间嵌入 `↓ 更多 worktree...` 提示
246
+ 7. **底栏**:快捷键提示 + 自动刷新倒计时
247
+
248
+ **Worktree 日期分组显示:**
249
+
250
+ Worktree 按创建日期分组(复用 `groupWorktreesByDate()`),每组前显示日期分隔线,格式为 `════ YYYY-MM-DD(相对日期) ════`,相对日期如"今天"、"昨天"、"3 天前"等(通过 `formatRelativeDate()` 格式化)。无法获取创建日期时归入"未知日期"分组。
251
+
252
+ 每个 worktree 条目的渲染内容与文本输出模式一致(分支名 + 变更状态标签、行数差异、本地提交数、同步状态、创建时间、验证状态),选中项前显示 `▶` 指示器(青色),未选中项前显示等宽空格占位。
253
+
254
+ **键盘操作:**
255
+
256
+ | 按键 | 操作 |
257
+ | ------- | ---------------------------------------- |
258
+ | `↑` | 向上导航,选中上一个 worktree |
259
+ | `↓` | 向下导航,选中下一个 worktree |
260
+ | `v` | 对选中 worktree 执行 `clawt validate` |
261
+ | `m` | 对选中 worktree 执行 `clawt merge` |
262
+ | `d` | 对选中 worktree 执行 `clawt remove` |
263
+ | `r` | 对选中 worktree 执行 `clawt resume` |
264
+ | `s` | 对选中 worktree 执行 `clawt sync` |
265
+ | `c` | 执行 `clawt cover`(从当前验证分支自动推导目标分支) |
266
+ | `f` | 手动刷新数据 |
267
+ | `q` | 退出面板 |
268
+ | `Ctrl+C`| 退出面板 |
269
+
270
+ **操作执行流程:**
271
+
272
+ 当用户按下操作快捷键(v/m/d/r/s/c)时,面板会:
273
+
274
+ 1. 暂停定时器和键盘监听
275
+ 2. 退出备选屏幕,恢复终端状态
276
+ 3. 以继承 stdio 的方式执行对应的 clawt 子命令(如 `clawt validate -b <branch>`)。其中 `c`(cover)命令不需要指定分支,直接执行 `clawt cover`
277
+ 4. 命令完成后,输出 `按 Enter 返回面板...` 提示
278
+ 5. 等待用户按 Enter 键
279
+ 6. 重新进入备选屏幕,刷新数据,恢复面板
280
+
281
+ 执行操作期间设置操作锁(`isOperating`),阻止其他按键响应。
282
+
283
+ **自动刷新机制:**
284
+
285
+ - 数据刷新间隔:每 5 秒自动调用 `collectStatus()` 重新收集数据(常量 `PANEL_REFRESH_INTERVAL_MS`)
286
+ - 倒计时更新:每 1 秒更新底栏的倒计时显示(常量 `PANEL_COUNTDOWN_INTERVAL_MS`)
287
+ - 刷新后保持选中位置:通过记录刷新前选中的分支名,在新数据中查找匹配的分支恢复选中位置;若分支已被删除则调整到安全范围
288
+
289
+ **滚动机制:**
290
+
291
+ - 滚动区域高度 = 终端行数 - 固定行数(标题 + 配置分支信息 + 快照摘要 + 顶部分隔线 + 底部分隔线 + 底栏 = `PANEL_FIXED_ROWS + 1`),最小 3 行
292
+ - 导航时自动调整滚动偏移(`scrollOffset`),确保选中项及其所属日期分组标题在可见区域内
293
+ - 溢出提示嵌入分隔线中间,不额外占用行数
294
+ - 终端 resize 时自动触发重绘
295
+
296
+ **渲染机制:**
297
+
298
+ - 使用备选屏幕(`ALT_SCREEN_ENTER` / `ALT_SCREEN_LEAVE`)避免污染原始终端内容
299
+ - 使用同步输出序列(`SYNC_OUTPUT_START` / `SYNC_OUTPUT_END`)防止闪烁
300
+ - 隐藏光标、禁用行换行,确保渲染效果整洁
301
+ - 注册 `exit` 事件兜底处理器,确保异常退出时终端状态被恢复
302
+ - 每行通过 `truncateToTerminalWidth()` 截断以适配终端宽度
303
+
304
+ **实现要点:**
305
+
306
+ - `InteractivePanel` 类定义在 `src/utils/interactive-panel.ts`,参照 `ProgressRenderer` 的生命周期模式实现
307
+ - 渲染函数集定义在 `src/utils/interactive-panel-render.ts`,导出以下函数:
308
+ - `buildPanelFrame()`:构建完整帧内容
309
+ - `buildGroupedWorktreeLines()`:按日期分组构建 worktree 行列表,返回 `PanelLine[]`
310
+ - `buildDisplayOrder()`:构建显示顺序到原始索引的映射
311
+ - `renderDateSeparator()`:渲染日期分隔线
312
+ - `renderWorktreeBlock()`:渲染单个 worktree 的多行块
313
+ - `renderSnapshotSummary()`:渲染快照摘要行
314
+ - `renderFooter()`:渲染底栏
315
+ - `calculateVisibleRows()`:计算滚动区域可用行数
316
+ - `renderConfiguredBranchLine()`:渲染配置分支信息行(内部函数,根据 `MainWorktreeStatus` 渲染不同状态)
317
+ - 面板常量定义在 `src/constants/interactive-panel.ts`:
318
+ - `PANEL_REFRESH_INTERVAL_MS`(5000)、`PANEL_COUNTDOWN_INTERVAL_MS`(1000)
319
+ - `SELECTED_INDICATOR`(`▶`)、`UNSELECTED_INDICATOR`(等宽空格)
320
+ - `KEY_ARROW_UP`、`KEY_ARROW_DOWN`、`KEY_CTRL_C`
321
+ - `PANEL_SHORTCUT_KEYS`(快捷键映射对象,包含 `VALIDATE`/`MERGE`/`DELETE`/`RESUME`/`SYNC`/`COVER`/`REFRESH`/`QUIT`)
322
+ - `PANEL_DATE_SEPARATOR_PREFIX`(`════`)
323
+ - `PANEL_FIXED_ROWS`(5,固定占用行数:配置分支信息 + 快照摘要 + 顶部分隔线 + 底部分隔线 + 底栏)
324
+ - `PANEL_SEPARATOR_MAX_WIDTH`(60,终端最大显示宽度限制)
325
+ - `PANEL_DATE_COLOR`(`#FF8C00`,日期分隔线高亮颜色,橙色)
326
+ - 面板消息常量定义在 `src/constants/messages/interactive-panel.ts`:
327
+ - `PANEL_FOOTER_SHORTCUTS`:底栏快捷键提示(从 `PANEL_SHORTCUT_KEYS` 自动生成)
328
+ - `PANEL_FOOTER_COUNTDOWN(seconds)`:底栏倒计时文本
329
+ - `PANEL_OVERFLOW_DOWN_HINT` / `PANEL_OVERFLOW_UP_HINT`:溢出提示
330
+ - `PANEL_SNAPSHOT_SUMMARY(total, orphaned)`:快照摘要文本
331
+ - `PANEL_NO_WORKTREES`:无 worktree 提示
332
+ - `PANEL_PRESS_ENTER_TO_RETURN`:操作后返回提示
333
+ - `PANEL_NOT_TTY`:非 TTY 降级提示
334
+ - `PANEL_TITLE(projectName)`:面板标题
335
+ - `PANEL_CONFIGURED_BRANCH(branchName)`:配置分支信息(正常状态,灰色)
336
+ - `PANEL_CONFIGURED_BRANCH_DELETED(branchName)`:配置分支信息(分支已删除,红色)
337
+ - `PANEL_CONFIGURED_BRANCH_MISMATCH(branchName)`:配置分支信息(分支不一致,黄色)
338
+ - `PANEL_NOT_INITIALIZED`:未初始化提示(灰色)
339
+ - `PanelLine` 接口(`src/utils/interactive-panel-render.ts`):面板行类型定义,包含 `type`(`'separator'` | `'worktree-content'`)、`text`、可选 `worktreeIndex`
340
+ - `collectStatus()` 函数已改为导出(`export`),以便 `InteractivePanel` 作为数据收集函数引用
341
+ - `handleStatus()` 为 `async` 函数,返回 `Promise<void>`
342
+ - `StatusOptions` 类型包含 `json?: boolean` 和 `interactive?: boolean` 字段
343
+ - 从 `src/utils/worktree-matcher.ts` 导出 `formatRelativeDate()`、`getWorktreeCreatedDate()` 和 `getWorktreeCreatedTime()`,供面板渲染和状态收集使用
344
+
345
+ ---
package/docs/sync.md ADDED
@@ -0,0 +1,128 @@
1
+ ### 5.12 将主分支代码同步到目标 Worktree
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ # 指定分支名(支持模糊匹配)
7
+ clawt sync -b <branchName>
8
+
9
+ # 不指定分支名(列出所有分支供选择)
10
+ clawt sync
11
+ ```
12
+
13
+ **参数:**
14
+
15
+ | 参数 | 必填 | 说明 |
16
+ | ---- | ---- | ------------------------------------------------------------------------ |
17
+ | `-b` | 否 | 要同步的分支名(支持模糊匹配,不传则列出所有分支供选择) |
18
+
19
+ **使用场景:**
20
+
21
+ 当目标 worktree 的分支需要使用主分支的最新代码继续工作时,通过 `clawt sync` 将主分支最新代码合并到目标 worktree。在新架构下,sync 不再是为了解决 validate 冲突(因为不会冲突了),而是纯粹的「将主分支最新代码同步到目标 worktree」的操作。
22
+
23
+ **运行流程:**
24
+
25
+ 1. **统一前置校验**:调用 `runPreChecks(PRE_CHECK_SYNC)` 执行以下校验:
26
+ - `requireMainWorktree`:校验当前目录是否在主 worktree 根目录
27
+ - `requireHead`:校验 HEAD 是否存在(仓库至少有一次 commit)
28
+ - `requireProjectConfig`:校验项目配置文件是否存在且合法(存在 `clawtMainWorkBranch` 配置)
29
+ - `ensureOnClawtMainWorkBranch`:确保当前处于主工作分支上,不在则自动切换。sync 命令需要从主分支发起合并操作,因此必须保证当前分支状态正确
30
+ 2. **解析目标 worktree**:根据 `-b` 参数解析目标 worktree,匹配策略如下:
31
+ - **未传 `-b` 参数**:
32
+ - 获取当前项目所有 worktree
33
+ - 无可用 worktree → 报错退出
34
+ - 仅 1 个 worktree → 直接使用,无需选择
35
+ - 多个 worktree → 通过交互式列表(Enquirer.Select)让用户选择
36
+ - **传了 `-b` 参数**:
37
+ 1. **精确匹配优先**:在 worktree 列表中查找分支名完全相同的 worktree,找到则直接使用
38
+ 2. **模糊匹配**(子串匹配,大小写不敏感):
39
+ - 唯一匹配 → 直接使用
40
+ - 多个匹配 → 通过交互式列表让用户从匹配结果中选择
41
+ 3. **无匹配** → 报错退出,并列出所有可用分支名
42
+ 3. 调用 `executeSyncForBranch(targetWorktreePath, branch)` 执行核心同步逻辑
43
+
44
+ #### `executeSyncForBranch` — sync 核心操作函数
45
+
46
+ `executeSyncForBranch(targetWorktreePath: string, branch: string): Promise<SyncResult>` 是从 `handleSync` 中抽取的核心同步逻辑(async 函数),不包含 worktree 解析交互,供 validate 等命令复用。
47
+
48
+ **接口定义:**
49
+
50
+ ```typescript
51
+ /** sync 核心操作的执行结果 */
52
+ export interface SyncResult {
53
+ /** 是否同步成功 */
54
+ success: boolean;
55
+ /** 是否存在合并冲突 */
56
+ hasConflict: boolean;
57
+ }
58
+ ```
59
+
60
+ **执行流程:**
61
+
62
+ 1. **获取主 worktree 路径和主分支名**:通过 `getGitTopLevel()` 获取主 worktree 路径(后续传给 `rebuildValidateBranch`),通过项目级配置 `clawtMainWorkBranch` 获取主工作分支名(不再通过 `getCurrentBranch` 动态获取,因为在新架构下主 worktree 可能处于验证分支上)
63
+ 2. **自动保存未提交变更**:检查目标 worktree 是否有未提交修改
64
+ - 有修改 → 自动执行 `git add . && git commit -m "<AUTO_SAVE_COMMIT_MESSAGE>"` 保存变更(commit message 由常量 `AUTO_SAVE_COMMIT_MESSAGE` 定义,值为 `chore: auto-save before sync`,同时用于 merge 命令的 squash 检测)
65
+ - 无修改 → 跳过
66
+ 3. **在目标 worktree 中合并主分支**:
67
+ ```bash
68
+ cd ~/.clawt/worktrees/<project>/<branchName>
69
+ git merge <mainBranch>
70
+ ```
71
+ 4. **冲突处理**:
72
+ - **有冲突** → 输出警告,提示用户进入目标 worktree 手动解决:
73
+ ```
74
+ 合并存在冲突,请进入目标 worktree 手动解决:
75
+ cd ~/.clawt/worktrees/<project>/<branchName>
76
+ 解决冲突后执行 git add . && git merge --continue
77
+ clawt validate -b <branch> 验证变更
78
+ ```
79
+ - 返回 `{ success: false, hasConflict: true }`
80
+ - **无冲突** → 继续
81
+ 5. **输出合并成功提示**:`✓ 已将 <mainBranch> 的最新代码同步到 <branchName>`
82
+ 6. **重建验证分支**(`rebuildValidateBranch`,async 函数):sync 将主分支合并到目标 worktree 后,目标分支的代码基点发生变化。为保持验证分支与目标分支基点一致,需要重建验证分支。
83
+ - 确保在主工作分支上创建验证分支,处理三种情况:
84
+ - **已在主工作分支上** → 直接重建
85
+ - **在验证分支上** → 验证分支修改可丢弃,清理工作区后自动切回主工作分支
86
+ - **在其他普通分支上** → 检查工作区是否干净,干净则直接切回主工作分支;不干净则交互处理(`handleDirtyWorkingDir`:reset / stash / exit)后切回
87
+ ```bash
88
+ # 情况 1:已在主工作分支上,无需切换
89
+
90
+ # 情况 2:在验证分支上,先清理工作区再切回主分支
91
+ git reset --hard
92
+ git clean -fd
93
+ git checkout <clawtMainWorkBranch>
94
+
95
+ # 情况 3:在其他分支上
96
+ # 如果工作区不干净,交互式处理(reset/stash/exit)
97
+ # 然后切回主工作分支
98
+ git checkout <clawtMainWorkBranch>
99
+
100
+ # 删除旧验证分支
101
+ git branch -D clawt-validate-<branchName>
102
+
103
+ # 基于当前主分支 HEAD 重新创建验证分支
104
+ git branch clawt-validate-<branchName>
105
+ ```
106
+ 7. **输出验证分支重建提示并返回结果**:输出 `验证分支 clawt-validate-<branchName> 已重建`,返回 `{ success: true, hasConflict: false }`
107
+
108
+ **设计说明 — 保留 validate 快照**:sync 合并成功后,不清除该分支的 validate 快照。因为 validate 使用三点 diff(`main...feature`),sync 后 merge-base 更新为合并提交,三点 diff 仍然只包含 feature 分支自身的修改,旧快照依然有效。增量 validate 时若检测到 HEAD 变化,会自动通过 diff-tree + apply 路径正确恢复暂存区状态。
109
+ 示意图:
110
+ 场景:将 HEAD(master) 合并到 branchName
111
+
112
+ 执行 git checkout branchName && git merge master 后:
113
+
114
+ A -- B -- C (HEAD/master)
115
+ / \
116
+ * M (branchName, merge commit)
117
+ \ /
118
+ D -- E ----
119
+
120
+ 此时执行 git diff HEAD...branchName:
121
+
122
+ - merge-base 变成了 C(因为合并后,HEAD 和 branchName 的最近共同祖先就是 C)
123
+
124
+ #### validate 中自动 sync 的联动
125
+
126
+ 当 validate 的 patch apply 失败(兜底场景)并触发自动 sync 时,sync 内部会自动重建验证分支,validate 流程结束后用户重新执行 validate 即可。
127
+
128
+ ---
package/docs/tasks.md ADDED
@@ -0,0 +1,74 @@
1
+ ### 5.22 任务文件管理
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ # 生成任务模板文件(默认输出到 clawt/tasks/ 目录)
7
+ clawt tasks init
8
+
9
+ # 指定输出路径
10
+ clawt tasks init [path]
11
+ ```
12
+
13
+ **子命令:**
14
+
15
+ | 子命令 | 说明 |
16
+ | ------ | ---- |
17
+ | `tasks init [path]` | 生成任务模板文件 |
18
+
19
+ **参数:**
20
+
21
+ | 参数 | 必填 | 说明 |
22
+ | ---- | ---- | ---- |
23
+ | `[path]` | 否 | 输出文件路径。不传时默认输出到 `clawt/tasks/clawt-tasks-<时间戳>.md` |
24
+
25
+ **功能说明:**
26
+
27
+ `tasks init` 子命令用于快速生成任务模板文件,方便用户创建 `clawt run -f` 所需的任务文件格式。模板文件包含格式说明注释和示例任务块。
28
+
29
+ **运行流程:**
30
+
31
+ 1. **确定输出路径**:
32
+ - 传了 `[path]` → 使用指定路径
33
+ - 未传 → 默认输出到 `clawt/tasks/clawt-tasks-<时间戳>.md`(由 `generateTaskFilename` 生成唯一文件名)
34
+ 2. **路径转换**:将路径转为绝对路径(`path.resolve`)
35
+ 3. **文件存在性校验**:如果目标文件已存在,抛出错误退出
36
+ 4. **创建父目录**:确保输出路径的父目录存在(`ensureDir`)
37
+ 5. **写入模板内容**:将 `TASK_TEMPLATE_CONTENT` 写入目标文件
38
+ 6. **输出成功提示**:提示文件已创建,并给出使用提示(如 `clawt run -f <path>`)
39
+
40
+ **模板文件内容:**
41
+
42
+ ```markdown
43
+ # Clawt 任务文件
44
+ #
45
+ # 使用方法: clawt run -f tasks.md
46
+ # 格式说明: 标签外的文本会被忽略,每个任务用 START/END 标签包裹
47
+ #
48
+ # 规则:
49
+ # 1. 每个任务块用 <!-- CLAWT-TASKS:START --> 和 <!-- CLAWT-TASKS:END --> 包裹
50
+ # 2. 块内 # branch: <分支名> 声明分支名(使用 -b 参数时可省略)
51
+ # 3. 块内其余行为任务描述(支持多行)
52
+
53
+ <!-- CLAWT-TASKS:START -->
54
+ # branch: feat-example-1
55
+ 在这里写第一个任务的描述
56
+ <!-- CLAWT-TASKS:END -->
57
+
58
+ <!-- CLAWT-TASKS:START -->
59
+ # branch: feat-example-2
60
+ 在这里写第二个任务的描述
61
+ 支持多行描述
62
+ <!-- CLAWT-TASKS:END -->
63
+ ```
64
+
65
+ **实现要点:**
66
+
67
+ - 命令注册函数 `registerTasksCommand` 位于 `src/commands/tasks.ts`
68
+ - 模板内容和常量定义在 `src/constants/tasks-template.ts`:
69
+ - `TASK_TEMPLATE_OUTPUT_DIR`(`clawt/tasks`):默认输出目录
70
+ - `TASK_TEMPLATE_FILENAME_PREFIX`(`clawt-tasks`):文件名前缀
71
+ - `TASK_TEMPLATE_CONTENT`:模板文件内容
72
+ - 消息常量定义在 `src/constants/messages/tasks.ts`
73
+
74
+ ---
@@ -0,0 +1,95 @@
1
+ ### 5.17 自动更新检查
2
+
3
+ CLI 在每次命令执行完毕后,根据配置项 `autoUpdate` 决定是否检查 npm registry 上的最新版本。当发现新版本时,以带边框的提示框在终端输出版本更新信息和升级命令。
4
+
5
+ #### 触发条件
6
+
7
+ - 配置项 `autoUpdate` 为 `true`(默认启用)
8
+ - 命令正常执行完毕后触发(在 `program.parseAsync()` 之后)
9
+
10
+ #### 检查流程
11
+
12
+ 1. 读取缓存文件 `~/.clawt/update-check.json`
13
+ 2. 判断缓存是否有效:
14
+ - 缓存不存在或解析失败 → 视为过期
15
+ - 缓存中的 `currentVersion` 与本地版本不一致 → 视为过期
16
+ - 距离上次检查超过 24 小时 → 视为过期
17
+ 3. **缓存有效**:直接使用缓存中的 `latestVersion` 与本地版本比较,有新版本则打印提示
18
+ 4. **缓存过期**:向 npm registry 发起 HTTPS 请求获取最新版本号(5 秒超时),更新缓存文件后判断并打印提示
19
+
20
+ #### 缓存文件
21
+
22
+ **路径:** `~/.clawt/update-check.json`
23
+
24
+ **结构:**
25
+
26
+ ```json
27
+ {
28
+ "lastCheck": 1709000000000,
29
+ "latestVersion": "2.18.0",
30
+ "currentVersion": "2.17.1"
31
+ }
32
+ ```
33
+
34
+ | 字段 | 类型 | 说明 |
35
+ | ---- | ---- | ---- |
36
+ | `lastCheck` | `number` | 上次检查时间戳(毫秒) |
37
+ | `latestVersion` | `string` | 从 registry 获取的最新版本号 |
38
+ | `currentVersion` | `string` | 检查时的本地版本号 |
39
+
40
+ #### 版本比较
41
+
42
+ 使用简易 semver 比较(不引入额外依赖),逐级比较 `major.minor.patch`:
43
+
44
+ - `latest > current` → 提示更新
45
+ - `latest <= current` → 不提示
46
+
47
+ #### 包管理器检测
48
+
49
+ 更新提示中会显示与用户安装方式匹配的升级命令。检测逻辑依次尝试:
50
+
51
+ 1. `pnpm list -g --depth=0 clawt` → 匹配则提示 `pnpm add -g clawt`
52
+ 2. `yarn global list --depth=0` → 输出含 `clawt` 则提示 `yarn global add clawt`
53
+ 3. 以上均未匹配 → 默认提示 `npm i -g clawt`
54
+
55
+ #### 提示框格式
56
+
57
+ 当检测到新版本时,输出带 Unicode 圆角边框的居中提示框:
58
+
59
+ ```
60
+ ╭──────────────────────────────────────────────╮
61
+ │ │
62
+ │ clawt 有新版本可用: 2.17.1 → 2.18.0 │
63
+ │ 执行 npm i -g clawt 进行更新 │
64
+ │ │
65
+ ╰──────────────────────────────────────────────╯
66
+ ```
67
+
68
+ 版本号和命令使用 chalk 着色:当前版本红色、最新版本绿色、更新命令青色。
69
+
70
+ #### 容错设计
71
+
72
+ 所有异常静默处理,不影响 CLI 正常功能:
73
+
74
+ - 网络请求失败或超时(5 秒) → 静默忽略
75
+ - registry 返回无效 JSON 或缺少 `version` 字段 → 静默忽略
76
+ - 缓存文件读写失败 → 静默忽略
77
+ - `checkForUpdates()` 入口函数的最外层 `try/catch` 确保任何未预期异常都不会中断 CLI
78
+
79
+ #### 常量定义
80
+
81
+ | 常量 | 值 | 位置 |
82
+ | ---- | -- | ---- |
83
+ | `UPDATE_CHECK_INTERVAL_MS` | `86400000`(24 小时) | `src/constants/update.ts` |
84
+ | `NPM_REGISTRY_URL` | `https://registry.npmjs.org/clawt/latest` | `src/constants/update.ts` |
85
+ | `NPM_REGISTRY_TIMEOUT_MS` | `5000` | `src/constants/update.ts` |
86
+ | `PACKAGE_NAME` | `clawt` | `src/constants/update.ts` |
87
+ | `UPDATE_CHECK_PATH` | `~/.clawt/update-check.json` | `src/constants/paths.ts` |
88
+
89
+ #### 实现说明
90
+
91
+ - 入口函数:`checkForUpdates()`(在 `src/utils/update-checker.ts`)
92
+ - 消息常量:`UPDATE_MESSAGES`、`UPDATE_COMMANDS`(在 `src/constants/messages/update.ts`)
93
+ - 入口调用点:`src/index.ts` 的 `main()` 异步函数中,`program.parseAsync()` 之后根据 `config.autoUpdate` 条件调用
94
+
95
+ ---