clawt 3.4.5 → 3.5.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 (45) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/README.md +0 -4
  3. package/dist/index.js +430 -306
  4. package/dist/postinstall.js +12 -1
  5. package/docs/alias.md +7 -1
  6. package/docs/completion.md +1 -1
  7. package/docs/config.md +4 -3
  8. package/docs/cover-validate.md +4 -3
  9. package/docs/create.md +28 -12
  10. package/docs/home.md +12 -8
  11. package/docs/init.md +16 -9
  12. package/docs/list.md +13 -7
  13. package/docs/merge.md +12 -12
  14. package/docs/remove.md +24 -13
  15. package/docs/reset.md +6 -4
  16. package/docs/resume.md +3 -4
  17. package/docs/status.md +75 -30
  18. package/docs/sync.md +26 -26
  19. package/docs/validate.md +13 -7
  20. package/package.json +1 -1
  21. package/src/commands/init.ts +6 -2
  22. package/src/commands/tasks.ts +51 -0
  23. package/src/constants/index.ts +3 -0
  24. package/src/constants/interactive-panel.ts +6 -0
  25. package/src/constants/messages/index.ts +4 -2
  26. package/src/constants/messages/interactive-panel.ts +12 -0
  27. package/src/constants/messages/tasks.ts +9 -0
  28. package/src/constants/tasks-template.ts +28 -0
  29. package/src/index.ts +2 -0
  30. package/src/types/command.ts +6 -0
  31. package/src/types/index.ts +1 -1
  32. package/src/utils/formatter.ts +19 -0
  33. package/src/utils/git-branch.ts +116 -0
  34. package/src/utils/git-core.ts +369 -0
  35. package/src/utils/git-worktree.ts +40 -0
  36. package/src/utils/git.ts +3 -521
  37. package/src/utils/index.ts +1 -1
  38. package/src/utils/interactive-panel-render.ts +12 -6
  39. package/src/utils/interactive-panel-state.ts +137 -0
  40. package/src/utils/interactive-panel.ts +44 -188
  41. package/src/utils/keyboard-controller.ts +48 -0
  42. package/src/utils/ui-prompts.ts +240 -0
  43. package/src/utils/worktree-matcher.ts +21 -251
  44. package/tests/unit/commands/tasks.test.ts +153 -0
  45. package/tests/unit/utils/formatter.test.ts +26 -1
package/docs/status.md CHANGED
@@ -15,7 +15,7 @@ clawt status [--json] [-i | --interactive]
15
15
 
16
16
  **使用场景:**
17
17
 
18
- 在管理多个 worktree 时,快速了解项目全局状态:主 worktree 当前分支及干净状态、所有 worktree 的变更情况和与主分支的同步状态、validate 快照摘要。
18
+ 在管理多个 worktree 时,快速了解项目全局状态:主 worktree 当前分支及干净状态、配置的主工作分支信息、所有 worktree 的变更情况和与主分支的同步状态、validate 快照摘要。
19
19
 
20
20
  交互式面板模式适用于需要持续监控项目状态并快速执行操作的场景,无需反复输入命令。
21
21
 
@@ -26,6 +26,8 @@ clawt status [--json] [-i | --interactive]
26
26
  - 获取当前分支名(`getCurrentBranch()`)
27
27
  - 检测工作区是否干净(`isWorkingDirClean()`)
28
28
  - 获取项目名(`getProjectName()`)
29
+ - 加载项目配置(`loadProjectConfig()`),读取配置的主工作分支名(`clawtMainWorkBranch`)
30
+ - 检测配置的主工作分支是否存在(`checkBranchExists()`)
29
31
  3. **收集各 worktree 详细状态**:
30
32
  - 获取项目所有 worktree(`getProjectWorktrees()`)
31
33
  - 对每个 worktree 收集以下信息:
@@ -33,7 +35,7 @@ clawt status [--json] [-i | --interactive]
33
35
  - **行数差异**(新增/删除行数,通过 `getDiffStat()` 获取)
34
36
  - **提交差异**(相对于主分支的领先提交数 `getCommitCountAhead()` 和落后提交数 `getCommitCountBehind()`)
35
37
  - **快照时间**(validate 快照文件的 mtime,通过 `getSnapshotModifiedTime()` 获取,返回 ISO 8601 时间字符串或 null)
36
- - **分支创建时间**(通过 `getBranchCreatedAt()` git reflog 获取分支创建时的时间戳)
38
+ - **创建时间**(通过 `getWorktreeCreatedTime()` 从文件系统 birthtime 获取 worktree 目录的创建时间)
37
39
  4. **收集 validate 快照摘要**:
38
40
  - 通过 `getProjectSnapshotBranches()` 扫描快照目录下的 `.tree` 文件获取所有存在快照的分支名
39
41
  - 统计快照总数和孤立快照数(对应 worktree 已不存在的快照)
@@ -46,6 +48,13 @@ clawt status [--json] [-i | --interactive]
46
48
 
47
49
  输出分为三个区块:主 Worktree、Worktree 列表、Validate 快照摘要。每个 worktree 条目每行展示一种信息。
48
50
 
51
+ 主 Worktree 区块会显示配置的主工作分支信息,根据状态有以下三种展示:
52
+ - **正常**(灰色):`主工作分支: <branchName>`
53
+ - **当前分支不一致**(黄色):`⚠ 主工作分支: <branchName>(当前分支不一致,如需更新请执行 clawt init)`
54
+ - **分支已不存在**(红色):`✗ 主工作分支: <branchName>(已不存在,请执行 clawt init 重新设置)`
55
+
56
+ 注意:当项目未初始化(`configuredMainBranch` 为 null)时不展示配置分支信息;当主 worktree 当前处于验证分支(`VALIDATE_BRANCH_PREFIX` 前缀)时不显示不一致警告。
57
+
49
58
  ```
50
59
  ════════════════════════════════════════
51
60
  项目状态总览: main-project
@@ -54,6 +63,7 @@ clawt status [--json] [-i | --interactive]
54
63
  ◆ 主 Worktree
55
64
  分支: main
56
65
  状态: ✓ 干净
66
+ 主工作分支: main
57
67
 
58
68
  ────────────────────────────────────────
59
69
 
@@ -96,9 +106,9 @@ clawt status [--json] [-i | --interactive]
96
106
  - 本地提交数(`N 个本地提交`):仅在有提交时展示,独立一行(黄色)
97
107
  - 与主分支同步状态:始终展示,独立一行(落后时显示黄色,同步时显示绿色)
98
108
 
99
- **分支创建时间行:**
109
+ **创建时间行:**
100
110
 
101
- - 通过 `getBranchCreatedAt()` git reflog 获取分支创建时间,以 `formatRelativeTime()` 格式化为中文相对时间(如"3 天前"、"2 小时前"、"刚刚")
111
+ - 通过 `getWorktreeCreatedTime()` 从文件系统 `birthtime` 获取 worktree 目录的创建时间,以 `formatRelativeTime()` 格式化为中文相对时间(如"3 天前"、"2 小时前"、"刚刚")
102
112
  - 展示为灰色文本 `创建于 X前`,无法获取时不展示
103
113
 
104
114
  **验证状态行:**
@@ -119,7 +129,9 @@ clawt status [--json] [-i | --interactive]
119
129
  "main": {
120
130
  "branch": "main",
121
131
  "isClean": true,
122
- "projectName": "main-project"
132
+ "projectName": "main-project",
133
+ "configuredMainBranch": "main",
134
+ "configuredBranchExists": true
123
135
  },
124
136
  "worktrees": [
125
137
  {
@@ -142,19 +154,40 @@ clawt status [--json] [-i | --interactive]
142
154
  }
143
155
  ```
144
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
+
145
167
  **实现要点:**
146
168
 
147
- - 类型定义在 `src/types/status.ts`:`WorktreeDetailedStatus`(`hasSnapshot` 已改为 `snapshotTime: string | null`,新增 `createdAt: string | null`)、`MainWorktreeStatus`、`SnapshotInfo`、`SnapshotSummary`(新增,包含 `total` 和 `orphaned`)、`StatusResult`(`snapshots` 已从 `SnapshotInfo[]` 改为 `SnapshotSummary`)
148
- - 消息常量在 `MESSAGES.STATUS_*` 系列,新增:
149
- - `STATUS_LAST_VALIDATED`:上次验证时间标签(如 `上次验证: 2 小时前`)
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)`:上次验证时间标签
150
178
  - `STATUS_NOT_VALIDATED`:未验证红色警示文本(`✗ 未验证`)
151
- - `STATUS_CREATED_AT`:分支创建时间标签(如 `创建于 3 天前`)
152
- - `STATUS_SNAPSHOT_ORPHANED`:改为接受数量参数的函数(如 `其中 1 个快照对应的 worktree 已不存在`)
153
- - `getBranchCreatedAt()` 是新增的工具函数(在 `src/utils/git.ts`),通过 `git reflog show <branch> --format=%cI` 获取 reflog 最后一条记录的时间戳(即分支创建时间),返回 ISO 8601 格式字符串或 null
154
- - `getSnapshotModifiedTime()` 是新增的工具函数(在 `src/utils/validate-snapshot.ts`),通过 `fs.statSync` 获取快照文件的修改时间(mtime),返回 UTC 时区的 ISO 8601 格式字符串(`toISOString()` 格式)或 null
155
- - `formatRelativeTime()` 是新增的格式化函数(在 `src/utils/formatter.ts`),将 ISO 8601 日期字符串转换为中文相对时间描述(如"3 天前"、"2 小时前"、"刚刚"),无效日期时返回 null
156
- - `getCommitCountBehind()` 是新增的工具函数(在 `src/utils/git.ts`),通过 `git rev-list --count <branch>..HEAD` 计算落后提交数
157
- - `getProjectSnapshotBranches()` 是新增的工具函数(在 `src/utils/validate-snapshot.ts`),通过扫描快照目录下的 `.tree` 文件提取分支名列表
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.ts`),通过 `git rev-list --count <branch>..HEAD` 计算落后提交数
188
+ - `getProjectSnapshotBranches()` 工具函数(在 `src/utils/validate-snapshot.ts`),通过扫描快照目录下的 `.tree` 文件提取分支名列表
189
+ - `loadProjectConfig()` 加载项目配置,读取 `clawtMainWorkBranch` 字段
190
+ - `checkBranchExists()` 检测分支是否存在
158
191
 
159
192
  **交互式面板模式(`-i` / `--interactive`):**
160
193
 
@@ -168,6 +201,7 @@ clawt status [--json] [-i | --interactive]
168
201
 
169
202
  ```
170
203
  项目状态总览: my-project
204
+ 主工作分支: main
171
205
  快照: 3 个(1 个孤立)
172
206
  ──────── ↑ 更多 worktree... ────────
173
207
  ════ 2026-03-01(2 天前) ════
@@ -194,21 +228,26 @@ clawt status [--json] [-i | --interactive]
194
228
  ✗ 未验证
195
229
 
196
230
  ──────── ↓ 更多 worktree... ────────
197
- [v]验证 [m]合并 [d]删除 [r]恢复 [s]同步 [f]刷新 [q]退出 (3s 后刷新)
231
+ [v]验证 [m]合并 [d]删除 [r]恢复 [s]同步 [c]覆盖 [f]刷新 [q]退出 (3s 后刷新)
198
232
  ```
199
233
 
200
234
  面板从上到下分为以下区域:
201
235
 
202
236
  1. **标题行**:显示项目名(`项目状态总览: <projectName>`)
203
- 2. **快照摘要行**:显示快照总数和孤立快照数
204
- 3. **顶部分隔线**:当存在向上溢出时,分隔线中间嵌入 `↑ 更多 worktree...` 提示
205
- 4. **Worktree 滚动区域**:按日期分组显示 worktree 列表,支持上下滚动
206
- 5. **底部分隔线**:当存在向下溢出时,分隔线中间嵌入 `↓ 更多 worktree...` 提示
207
- 6. **底栏**:快捷键提示 + 自动刷新倒计时
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. **底栏**:快捷键提示 + 自动刷新倒计时
208
247
 
209
248
  **Worktree 日期分组显示:**
210
249
 
211
- Worktree 按创建日期分组(复用 `groupWorktreesByDate()`),每组前显示日期分隔线,格式为 `════ YYYY-MM-DD(相对日期) ════`,相对日期如"今天"、"昨天"、"3 天前"等(通过 `formatRelativeDate()` 格式化)。
250
+ Worktree 按创建日期分组(复用 `groupWorktreesByDate()`),每组前显示日期分隔线,格式为 `════ YYYY-MM-DD(相对日期) ════`,相对日期如"今天"、"昨天"、"3 天前"等(通过 `formatRelativeDate()` 格式化)。无法获取创建日期时归入"未知日期"分组。
212
251
 
213
252
  每个 worktree 条目的渲染内容与文本输出模式一致(分支名 + 变更状态标签、行数差异、本地提交数、同步状态、创建时间、验证状态),选中项前显示 `▶` 指示器(青色),未选中项前显示等宽空格占位。
214
253
 
@@ -223,17 +262,18 @@ Worktree 按创建日期分组(复用 `groupWorktreesByDate()`),每组前
223
262
  | `d` | 对选中 worktree 执行 `clawt remove` |
224
263
  | `r` | 对选中 worktree 执行 `clawt resume` |
225
264
  | `s` | 对选中 worktree 执行 `clawt sync` |
265
+ | `c` | 执行 `clawt cover`(从当前验证分支自动推导目标分支) |
226
266
  | `f` | 手动刷新数据 |
227
267
  | `q` | 退出面板 |
228
268
  | `Ctrl+C`| 退出面板 |
229
269
 
230
270
  **操作执行流程:**
231
271
 
232
- 当用户按下操作快捷键(v/m/d/r/s)时,面板会:
272
+ 当用户按下操作快捷键(v/m/d/r/s/c)时,面板会:
233
273
 
234
274
  1. 暂停定时器和键盘监听
235
275
  2. 退出备选屏幕,恢复终端状态
236
- 3. 以继承 stdio 的方式执行对应的 clawt 子命令(如 `clawt validate -b <branch>`)
276
+ 3. 以继承 stdio 的方式执行对应的 clawt 子命令(如 `clawt validate -b <branch>`)。其中 `c`(cover)命令不需要指定分支,直接执行 `clawt cover`
237
277
  4. 命令完成后,输出 `按 Enter 返回面板...` 提示
238
278
  5. 等待用户按 Enter 键
239
279
  6. 重新进入备选屏幕,刷新数据,恢复面板
@@ -248,7 +288,7 @@ Worktree 按创建日期分组(复用 `groupWorktreesByDate()`),每组前
248
288
 
249
289
  **滚动机制:**
250
290
 
251
- - 滚动区域高度 = 终端行数 - 固定行数(标题 + 快照摘要 + 顶部分隔线 + 底部分隔线 + 底栏 = `PANEL_FIXED_ROWS + 1`),最小 3 行
291
+ - 滚动区域高度 = 终端行数 - 固定行数(标题 + 配置分支信息 + 快照摘要 + 顶部分隔线 + 底部分隔线 + 底栏 = `PANEL_FIXED_ROWS + 1`),最小 3 行
252
292
  - 导航时自动调整滚动偏移(`scrollOffset`),确保选中项及其所属日期分组标题在可见区域内
253
293
  - 溢出提示嵌入分隔线中间,不额外占用行数
254
294
  - 终端 resize 时自动触发重绘
@@ -273,13 +313,14 @@ Worktree 按创建日期分组(复用 `groupWorktreesByDate()`),每组前
273
313
  - `renderSnapshotSummary()`:渲染快照摘要行
274
314
  - `renderFooter()`:渲染底栏
275
315
  - `calculateVisibleRows()`:计算滚动区域可用行数
316
+ - `renderConfiguredBranchLine()`:渲染配置分支信息行(内部函数,根据 `MainWorktreeStatus` 渲染不同状态)
276
317
  - 面板常量定义在 `src/constants/interactive-panel.ts`:
277
318
  - `PANEL_REFRESH_INTERVAL_MS`(5000)、`PANEL_COUNTDOWN_INTERVAL_MS`(1000)
278
319
  - `SELECTED_INDICATOR`(`▶`)、`UNSELECTED_INDICATOR`(等宽空格)
279
320
  - `KEY_ARROW_UP`、`KEY_ARROW_DOWN`、`KEY_CTRL_C`
280
- - `PANEL_SHORTCUT_KEYS`(快捷键映射对象)
321
+ - `PANEL_SHORTCUT_KEYS`(快捷键映射对象,包含 `VALIDATE`/`MERGE`/`DELETE`/`RESUME`/`SYNC`/`COVER`/`REFRESH`/`QUIT`)
281
322
  - `PANEL_DATE_SEPARATOR_PREFIX`(`════`)
282
- - `PANEL_FIXED_ROWS`(4,固定占用行数)
323
+ - `PANEL_FIXED_ROWS`(5,固定占用行数:配置分支信息 + 快照摘要 + 顶部分隔线 + 底部分隔线 + 底栏)
283
324
  - 面板消息常量定义在 `src/constants/messages/interactive-panel.ts`:
284
325
  - `PANEL_FOOTER_SHORTCUTS`:底栏快捷键提示(从 `PANEL_SHORTCUT_KEYS` 自动生成)
285
326
  - `PANEL_FOOTER_COUNTDOWN(seconds)`:底栏倒计时文本
@@ -289,10 +330,14 @@ Worktree 按创建日期分组(复用 `groupWorktreesByDate()`),每组前
289
330
  - `PANEL_PRESS_ENTER_TO_RETURN`:操作后返回提示
290
331
  - `PANEL_NOT_TTY`:非 TTY 降级提示
291
332
  - `PANEL_TITLE(projectName)`:面板标题
333
+ - `PANEL_CONFIGURED_BRANCH(branchName)`:配置分支信息(正常状态,灰色)
334
+ - `PANEL_CONFIGURED_BRANCH_DELETED(branchName)`:配置分支信息(分支已删除,红色)
335
+ - `PANEL_CONFIGURED_BRANCH_MISMATCH(branchName)`:配置分支信息(分支不一致,黄色)
336
+ - `PANEL_NOT_INITIALIZED`:未初始化提示(灰色)
292
337
  - `PanelLine` 接口(`src/utils/interactive-panel-render.ts`):面板行类型定义,包含 `type`(`'separator'` | `'worktree-content'`)、`text`、可选 `worktreeIndex`
293
338
  - `collectStatus()` 函数已改为导出(`export`),以便 `InteractivePanel` 作为数据收集函数引用
294
- - `handleStatus()` 改为 `async` 函数,返回 `Promise<void>`
295
- - `StatusOptions` 类型新增 `interactive?: boolean` 字段
296
- - 从 `src/utils/worktree-matcher.ts` 新导出 `formatRelativeDate()` 和 `getWorktreeCreatedDate()`,供面板渲染使用
339
+ - `handleStatus()` `async` 函数,返回 `Promise<void>`
340
+ - `StatusOptions` 类型包含 `json?: boolean` 和 `interactive?: boolean` 字段
341
+ - 从 `src/utils/worktree-matcher.ts` 导出 `formatRelativeDate()`、`getWorktreeCreatedDate()` 和 `getWorktreeCreatedTime()`,供面板渲染和状态收集使用
297
342
 
298
343
  ---
package/docs/sync.md CHANGED
@@ -22,10 +22,12 @@ clawt sync
22
22
 
23
23
  **运行流程:**
24
24
 
25
- 1. **主 worktree 校验** (2.1)
26
- 2. **项目配置校验**:调用 `requireProjectConfig()` 确保项目已初始化(存在 `clawtMainWorkBranch` 配置)
27
- 3. **确保在主工作分支上**:`handleSync` 在执行核心逻辑前,调用 `ensureOnMainWorkBranch()` 确保当前处于主工作分支上。sync 命令需要从主分支发起合并操作,因此必须保证当前分支状态正确。
28
- 4. **解析目标 worktree**:根据 `-b` 参数解析目标 worktree,匹配策略如下:
25
+ 1. **统一前置校验**:调用 `runPreChecks(PRE_CHECK_SYNC)` 执行以下校验:
26
+ - `requireMainWorktree`:校验当前目录是否在主 worktree 根目录
27
+ - `requireHead`:校验 HEAD 是否存在(仓库至少有一次 commit)
28
+ - `requireProjectConfig`:校验项目配置文件是否存在且合法(存在 `clawtMainWorkBranch` 配置)
29
+ - `ensureOnClawtMainWorkBranch`:确保当前处于主工作分支上,不在则自动切换。sync 命令需要从主分支发起合并操作,因此必须保证当前分支状态正确
30
+ 2. **解析目标 worktree**:根据 `-b` 参数解析目标 worktree,匹配策略如下:
29
31
  - **未传 `-b` 参数**:
30
32
  - 获取当前项目所有 worktree
31
33
  - 无可用 worktree → 报错退出
@@ -37,7 +39,7 @@ clawt sync
37
39
  - 唯一匹配 → 直接使用
38
40
  - 多个匹配 → 通过交互式列表让用户从匹配结果中选择
39
41
  3. **无匹配** → 报错退出,并列出所有可用分支名
40
- 5. 调用 `executeSyncForBranch(targetWorktreePath, branch)` 执行核心同步逻辑
42
+ 3. 调用 `executeSyncForBranch(targetWorktreePath, branch)` 执行核心同步逻辑
41
43
 
42
44
  #### `executeSyncForBranch` — sync 核心操作函数
43
45
 
@@ -57,7 +59,7 @@ export interface SyncResult {
57
59
 
58
60
  **执行流程:**
59
61
 
60
- 1. **获取主分支名**:通过项目级配置 `clawtMainWorkBranch` 获取主工作分支名(不再通过 `getCurrentBranch` 动态获取,因为在新架构下主 worktree 可能处于验证分支上)
62
+ 1. **获取主 worktree 路径和主分支名**:通过 `getGitTopLevel()` 获取主 worktree 路径(后续传给 `rebuildValidateBranch`),通过项目级配置 `clawtMainWorkBranch` 获取主工作分支名(不再通过 `getCurrentBranch` 动态获取,因为在新架构下主 worktree 可能处于验证分支上)
61
63
  2. **自动保存未提交变更**:检查目标 worktree 是否有未提交修改
62
64
  - 有修改 → 自动执行 `git add . && git commit -m "<AUTO_SAVE_COMMIT_MESSAGE>"` 保存变更(commit message 由常量 `AUTO_SAVE_COMMIT_MESSAGE` 定义,值为 `chore: auto-save before sync`,同时用于 merge 命令的 squash 检测)
63
65
  - 无修改 → 跳过
@@ -76,21 +78,7 @@ export interface SyncResult {
76
78
  ```
77
79
  - 返回 `{ success: false, hasConflict: true }`
78
80
  - **无冲突** → 继续
79
- 5. **保留 validate 快照**:sync 合并成功后,不清除该分支的 validate 快照。因为 validate 使用三点 diff(`main...feature`),sync 后 merge-base 更新为合并提交,三点 diff 仍然只包含 feature 分支自身的修改,旧快照依然有效。增量 validate 时若检测到 HEAD 变化,会自动通过 diff-tree + apply 路径正确恢复暂存区状态。
80
- 示意图:
81
- 场景:将 HEAD(master) 合并到 branchName
82
-
83
- 执行 git checkout branchName && git merge master 后:
84
-
85
- A -- B -- C (HEAD/master)
86
- / \
87
- * M (branchName, merge commit)
88
- \ /
89
- D -- E ----
90
-
91
- 此时执行 git diff HEAD...branchName:
92
-
93
- - merge-base 变成了 C(因为合并后,HEAD 和 branchName 的最近共同祖先就是 C)
81
+ 5. **输出合并成功提示**:`✓ 已将 <mainBranch> 的最新代码同步到 <branchName>`
94
82
  6. **重建验证分支**(`rebuildValidateBranch`,async 函数):sync 将主分支合并到目标 worktree 后,目标分支的代码基点发生变化。为保持验证分支与目标分支基点一致,需要重建验证分支。
95
83
  - 确保在主工作分支上创建验证分支,处理三种情况:
96
84
  - **已在主工作分支上** → 直接重建
@@ -115,11 +103,23 @@ export interface SyncResult {
115
103
  # 基于当前主分支 HEAD 重新创建验证分支
116
104
  git branch clawt-validate-<branchName>
117
105
  ```
118
- 7. **输出成功提示**,然后执行 `rebuildValidateBranch` 重建验证分支,再输出验证分支重建提示,最后返回 `{ success: true, hasConflict: false }`:
119
- ```
120
- 已将 <mainBranch> 的最新代码同步到 <branchName>
121
- 验证分支 clawt-validate-<branchName> 已重建
122
- ```
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
123
 
124
124
  #### validate 中自动 sync 的联动
125
125
 
package/docs/validate.md CHANGED
@@ -38,16 +38,18 @@ validate 命令引入了**快照(snapshot)机制**来支持增量对比。
38
38
 
39
39
  当指定 `--clean` 选项时,执行清理逻辑后直接返回,不进入常规 validate 流程:
40
40
 
41
- 1. **主 worktree 校验** (2.1)
41
+ 1. **前置校验**:主 worktree 校验 + HEAD 存在性校验 + 项目配置校验(`runPreChecks`)
42
42
  2. **解析目标 worktree**:通过模糊匹配解析目标分支(匹配策略同下文常规 validate 流程中的描述)
43
43
  3. 如果配置项 `confirmDestructiveOps` 为 `true`,提示确认(显示即将执行的危险指令和操作后果),用户取消则退出
44
44
  4. 如果主 worktree 有未提交更改,执行 `git reset --hard` + `git clean -fd` 清空
45
- 5. **(新增)** 确保当前处于主工作分支上(`ensureOnMainWorkBranch`):如果在验证分支上,清理后切回;如果在其他分支上,交互处理脏工作区后切回
45
+ 5. 确保当前处于主工作分支上(`ensureOnMainWorkBranch`):如果在验证分支上,清理后切回;如果在其他分支上,交互处理脏工作区后切回
46
46
  6. 删除对应分支的快照文件
47
47
  7. 输出清理成功提示
48
48
 
49
49
  #### 首次 validate(无历史快照)
50
50
 
51
+ > 常规 validate(首次和增量)执行前均会先进行前置校验:主 worktree 校验 + HEAD 存在性校验 + 项目配置校验(`runPreChecks`)。
52
+
51
53
  ##### 步骤 0:解析目标 worktree
52
54
 
53
55
  根据 `-b` 参数解析目标 worktree,匹配策略如下:
@@ -153,9 +155,9 @@ git restore --staged .
153
155
 
154
156
  **实现要点:**
155
157
 
156
- - `migrateChangesViaPatch()` 返回类型从 `void` 改为 `{ success: boolean }`,patch apply 失败时返回 `{ success: false }` 而非抛出异常
157
- - `handleFirstValidate()` 和 `handleIncrementalValidate()` 从同步函数改为 `async` 函数,以支持交互式确认
158
- - `handlePatchApplyFailure()` 为新增的异步函数(`src/commands/validate.ts`),负责 patch 失败后的交互逻辑
158
+ - `migrateChangesViaPatch()`(`src/utils/validate-core.ts`)返回 `{ success: boolean }`,patch apply 失败时返回 `{ success: false }` 而非抛出异常
159
+ - `handleFirstValidate()` 和 `handleIncrementalValidate()` `async` 函数,支持交互式确认
160
+ - `handlePatchApplyFailure()`(`src/commands/validate.ts`)为异步函数,负责 patch 失败后的交互逻辑
159
161
  - 消息常量:`MESSAGES.VALIDATE_CONFIRM_AUTO_SYNC`、`MESSAGES.VALIDATE_AUTO_SYNC_START`、`MESSAGES.VALIDATE_AUTO_SYNC_DECLINED`(`src/constants/messages/validate.ts`)
160
162
 
161
163
  ##### 步骤 5:保存快照为 git tree 对象
@@ -166,6 +168,7 @@ git restore --staged .
166
168
  git add .
167
169
  git write-tree # → 返回 tree hash,写入 ~/.clawt/validate-snapshots/<project>/<branchName>.tree
168
170
  git rev-parse HEAD # → 返回验证分支的 HEAD commit hash,写入 ~/.clawt/validate-snapshots/<project>/<branchName>.head
171
+ # 同时写入 ~/.clawt/validate-snapshots/<project>/<branchName>.staged(首次 validate 为空字符串)
169
172
  git restore --staged .
170
173
  ```
171
174
 
@@ -279,7 +282,7 @@ clawt validate -b feature-scheme-1 -r "pnpm test & pnpm build"
279
282
 
280
283
  - 命令解析:`parseParallelCommands()`(`src/utils/shell.ts`)
281
284
  - 并行执行:`runParallelCommands()`(`src/utils/shell.ts`),返回 `ParallelCommandResult[]`
282
- - 结果汇总:`reportParallelResults()`(`src/commands/validate.ts`)
285
+ - 结果汇总:`reportParallelResults()`(`src/utils/validate-runner.ts`)
283
286
  - 消息常量:`MESSAGES.VALIDATE_PARALLEL_*` 系列(`src/constants/messages/validate.ts`)
284
287
  - 命令解析优先级:`resolveRunCommand()`(`src/commands/validate.ts`)负责解析最终要执行的命令,优先使用 `-r` 参数,否则从项目配置读取 `validateRunCommand`(通过 `getValidateRunCommand()`,`src/utils/project-config.ts`)
285
288
 
@@ -324,7 +327,7 @@ git rev-parse HEAD # → currentHeadCommitHash
324
327
  hasNewChanges = (newTreeHash !== oldTreeHash) || (oldHeadCommitHash !== currentHeadCommitHash)
325
328
  ```
326
329
 
327
- - **无新变更**(tree hash 和 HEAD 均未变化)→ 不更新快照,直接通过 `git read-tree <oldStagedTreeHash>` 恢复上次 validate 结束时的暂存区状态,输出提示后返回
330
+ - **无新变更**(tree hash 和 HEAD 均未变化)→ 不更新快照;如果旧快照记录了 `oldStagedTreeHash`(非空),通过 `git read-tree <oldStagedTreeHash>` 恢复上次 validate 结束时的暂存区状态(恢复失败仅输出 warn 日志,不影响流程);输出提示后返回
328
331
  - **有新变更** → 继续步骤 6
329
332
 
330
333
  > 无变更检测避免了重复 validate 时不必要的快照更新和暂存区重载操作。恢复上次暂存区状态后,用户看到的 `git diff` 结果与上次 validate 结束时完全一致。
@@ -397,8 +400,11 @@ echo <newStagedTreeHash>
397
400
 
398
401
  # 增量无变更
399
402
  分支 feature-scheme-1 自上次 validate 以来没有新的变更,已恢复到上次验证状态
403
+ ✓ 已切换到验证分支 clawt-validate-feature-scheme-1 并应用分支 feature-scheme-1 的变更
404
+ 可以开始验证了
400
405
 
401
406
  # 增量降级为全量
407
+ 增量对比失败,已降级为全量模式
402
408
  ✓ 已切换到验证分支 clawt-validate-feature-scheme-1 并应用分支 feature-scheme-1 的变更
403
409
  可以开始验证了
404
410
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawt",
3
- "version": "3.4.5",
3
+ "version": "3.5.0",
4
4
  "description": "本地并行执行多个Claude Code Agent任务,融合 Git Worktree 与 Claude Code CLI 的命令行工具",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -79,8 +79,12 @@ async function handleInit(options: InitOptions): Promise<void> {
79
79
 
80
80
  logger.info(`init 命令执行,主工作分支: ${branchName}`);
81
81
 
82
- // 保存项目配置
83
- saveProjectConfig({ clawtMainWorkBranch: branchName });
82
+ // 合并现有配置,仅更新 clawtMainWorkBranch
83
+ const updatedConfig: ProjectConfig = {
84
+ ...existingConfig,
85
+ clawtMainWorkBranch: branchName,
86
+ };
87
+ saveProjectConfig(updatedConfig);
84
88
 
85
89
  if (existingConfig) {
86
90
  printSuccess(MESSAGES.INIT_UPDATED(existingConfig.clawtMainWorkBranch, branchName));
@@ -0,0 +1,51 @@
1
+ import { resolve, dirname, join } from 'node:path';
2
+ import { existsSync, writeFileSync } from 'node:fs';
3
+ import type { Command } from 'commander';
4
+ import { MESSAGES, TASK_TEMPLATE_OUTPUT_DIR, TASK_TEMPLATE_FILENAME_PREFIX, TASK_TEMPLATE_CONTENT } from '../constants/index.js';
5
+ import { ClawtError } from '../errors/index.js';
6
+ import { logger } from '../logger/index.js';
7
+ import { printSuccess, printHint, ensureDir, generateTaskFilename } from '../utils/index.js';
8
+
9
+ /**
10
+ * 注册 tasks 命令组及其子命令
11
+ * @param {Command} program - Commander 实例
12
+ */
13
+ export function registerTasksCommand(program: Command): void {
14
+ const taskCmd = program
15
+ .command('tasks')
16
+ .description('任务文件管理');
17
+
18
+ taskCmd
19
+ .command('init')
20
+ .description('生成任务模板文件')
21
+ .argument('[path]', '输出文件路径')
22
+ .action(async (path?: string) => {
23
+ // 未指定路径时,默认输出到 clawt/tasks/ 目录下
24
+ const filePath = path ?? join(TASK_TEMPLATE_OUTPUT_DIR, generateTaskFilename(TASK_TEMPLATE_FILENAME_PREFIX));
25
+ await handleTasksInit(filePath);
26
+ });
27
+ }
28
+
29
+ /**
30
+ * 处理 tasks init 子命令:生成任务模板文件
31
+ * @param {string} filePath - 输出文件路径
32
+ */
33
+ async function handleTasksInit(filePath: string): Promise<void> {
34
+ const absolutePath = resolve(filePath);
35
+
36
+ logger.info(`tasks init 命令执行,目标文件: ${absolutePath}`);
37
+
38
+ // 检查文件是否已存在
39
+ if (existsSync(absolutePath)) {
40
+ throw new ClawtError(MESSAGES.TASK_INIT_FILE_EXISTS(filePath));
41
+ }
42
+
43
+ // 确保父目录存在
44
+ ensureDir(dirname(absolutePath));
45
+
46
+ // 写入模板内容
47
+ writeFileSync(absolutePath, TASK_TEMPLATE_CONTENT, 'utf-8');
48
+
49
+ printSuccess(MESSAGES.TASK_INIT_SUCCESS(filePath));
50
+ printHint(MESSAGES.TASK_INIT_HINT(filePath));
51
+ }
@@ -42,5 +42,8 @@ export {
42
42
  PANEL_SHORTCUT_KEYS,
43
43
  PANEL_DATE_SEPARATOR_PREFIX,
44
44
  PANEL_FIXED_ROWS,
45
+ PANEL_SEPARATOR_MAX_WIDTH,
46
+ PANEL_DATE_COLOR,
45
47
  } from './interactive-panel.js';
46
48
  export { PRE_CHECK_CREATE, PRE_CHECK_RUN, PRE_CHECK_DRY_RUN, PRE_CHECK_MERGE, PRE_CHECK_SYNC, PRE_CHECK_RESUME } from './pre-checks.js';
49
+ export { TASK_TEMPLATE_OUTPUT_DIR, TASK_TEMPLATE_FILENAME_PREFIX, TASK_TEMPLATE_CONTENT } from './tasks-template.js';
@@ -44,3 +44,9 @@ export const PANEL_DATE_SEPARATOR_PREFIX = '════';
44
44
 
45
45
  /** 固定占用行数(配置分支信息 + 快照摘要 + 顶部分隔线 + 底部分隔线 + 底栏) */
46
46
  export const PANEL_FIXED_ROWS = 5;
47
+
48
+ /** 终端最大显示宽度限制 */
49
+ export const PANEL_SEPARATOR_MAX_WIDTH = 60;
50
+
51
+ /** 日期分隔线高亮颜色(橙色) */
52
+ export const PANEL_DATE_COLOR = '#FF8C00';
@@ -16,11 +16,12 @@ import { UPDATE_MESSAGES, UPDATE_COMMANDS } from './update.js';
16
16
  import { INIT_MESSAGES } from './init.js';
17
17
  import { COVER_VALIDATE_MESSAGES } from './cover-validate.js';
18
18
  import { HOME_MESSAGES } from './home.js';
19
- import { PANEL_FOOTER_SHORTCUTS, PANEL_FOOTER_COUNTDOWN, PANEL_OVERFLOW_DOWN_HINT, PANEL_OVERFLOW_UP_HINT, PANEL_SNAPSHOT_SUMMARY, PANEL_NO_WORKTREES as PANEL_NO_WORKTREES_MSG, PANEL_PRESS_ENTER_TO_RETURN, PANEL_NOT_TTY, PANEL_TITLE, PANEL_CONFIGURED_BRANCH, PANEL_CONFIGURED_BRANCH_DELETED, PANEL_CONFIGURED_BRANCH_MISMATCH, PANEL_NOT_INITIALIZED } from './interactive-panel.js';
19
+ import { TASKS_CMD_MESSAGES } from './tasks.js';
20
+ import { PANEL_FOOTER_SHORTCUTS, PANEL_FOOTER_COUNTDOWN, PANEL_OVERFLOW_DOWN_HINT, PANEL_OVERFLOW_UP_HINT, PANEL_SNAPSHOT_SUMMARY, PANEL_NO_WORKTREES as PANEL_NO_WORKTREES_MSG, PANEL_PRESS_ENTER_TO_RETURN, PANEL_NOT_TTY, PANEL_TITLE, PANEL_CONFIGURED_BRANCH, PANEL_CONFIGURED_BRANCH_DELETED, PANEL_CONFIGURED_BRANCH_MISMATCH, PANEL_NOT_INITIALIZED, PANEL_UNKNOWN_DATE, PANEL_SYNCED_WITH_MAIN, PANEL_COMMITS_AHEAD, PANEL_COMMITS_BEHIND } from './interactive-panel.js';
20
21
 
21
22
  export { CONFIG_ALIAS_DISABLED_HINT };
22
23
  export { UPDATE_MESSAGES, UPDATE_COMMANDS };
23
- export { PANEL_FOOTER_SHORTCUTS, PANEL_FOOTER_COUNTDOWN, PANEL_OVERFLOW_DOWN_HINT, PANEL_OVERFLOW_UP_HINT, PANEL_SNAPSHOT_SUMMARY, PANEL_NO_WORKTREES_MSG, PANEL_PRESS_ENTER_TO_RETURN, PANEL_NOT_TTY, PANEL_TITLE, PANEL_CONFIGURED_BRANCH, PANEL_CONFIGURED_BRANCH_DELETED, PANEL_CONFIGURED_BRANCH_MISMATCH, PANEL_NOT_INITIALIZED };
24
+ export { PANEL_FOOTER_SHORTCUTS, PANEL_FOOTER_COUNTDOWN, PANEL_OVERFLOW_DOWN_HINT, PANEL_OVERFLOW_UP_HINT, PANEL_SNAPSHOT_SUMMARY, PANEL_NO_WORKTREES_MSG, PANEL_PRESS_ENTER_TO_RETURN, PANEL_NOT_TTY, PANEL_TITLE, PANEL_CONFIGURED_BRANCH, PANEL_CONFIGURED_BRANCH_DELETED, PANEL_CONFIGURED_BRANCH_MISMATCH, PANEL_NOT_INITIALIZED, PANEL_UNKNOWN_DATE, PANEL_SYNCED_WITH_MAIN, PANEL_COMMITS_AHEAD, PANEL_COMMITS_BEHIND };
24
25
 
25
26
  /**
26
27
  * 提示消息模板
@@ -44,4 +45,5 @@ export const MESSAGES = {
44
45
  ...INIT_MESSAGES,
45
46
  ...COVER_VALIDATE_MESSAGES,
46
47
  ...HOME_MESSAGES,
48
+ ...TASKS_CMD_MESSAGES,
47
49
  } as const;
@@ -87,3 +87,15 @@ export const PANEL_CONFIGURED_BRANCH_MISMATCH = (branchName: string): string =>
87
87
 
88
88
  /** 面板配置分支信息(未初始化) */
89
89
  export const PANEL_NOT_INITIALIZED = chalk.gray('未初始化(执行 clawt init 设置主工作分支)');
90
+
91
+ /** 交互面板日期分隔线:未知日期文案 */
92
+ export const PANEL_UNKNOWN_DATE = '未知日期';
93
+
94
+ /** 交互面板:与主分支同步 */
95
+ export const PANEL_SYNCED_WITH_MAIN = '与主分支同步';
96
+
97
+ /** 交互面板:本地提交数量提示 */
98
+ export const PANEL_COMMITS_AHEAD = (count: number): string => `${count} 个本地提交`;
99
+
100
+ /** 交互面板:落后主分支提交数量提示 */
101
+ export const PANEL_COMMITS_BEHIND = (count: number): string => `落后主分支 ${count} 个提交`;
@@ -0,0 +1,9 @@
1
+ /** tasks 命令相关提示消息 */
2
+ export const TASKS_CMD_MESSAGES = {
3
+ /** 任务模板文件已存在 */
4
+ TASK_INIT_FILE_EXISTS: (path: string) => `文件已存在: ${path},如需覆盖请先删除`,
5
+ /** 任务模板生成成功 */
6
+ TASK_INIT_SUCCESS: (path: string) => `✓ 任务模板已生成: ${path}`,
7
+ /** 任务模板使用提示 */
8
+ TASK_INIT_HINT: (path: string) => `使用 clawt run -f ${path} 执行任务`,
9
+ } as const;
@@ -0,0 +1,28 @@
1
+ /** 任务模板默认输出目录 */
2
+ export const TASK_TEMPLATE_OUTPUT_DIR = 'clawt/tasks';
3
+
4
+ /** 任务模板文件名前缀 */
5
+ export const TASK_TEMPLATE_FILENAME_PREFIX = 'clawt-tasks';
6
+
7
+ /** 任务模板文件内容 */
8
+ export const TASK_TEMPLATE_CONTENT = `# Clawt 任务文件
9
+ #
10
+ # 使用方法: clawt run -f tasks.md
11
+ # 格式说明: 标签外的文本会被忽略,每个任务用 START/END 标签包裹
12
+ #
13
+ # 规则:
14
+ # 1. 每个任务块用 <!-- CLAWT-TASKS:START --> 和 <!-- CLAWT-TASKS:END --> 包裹
15
+ # 2. 块内 # branch: <分支名> 声明分支名(使用 -b 参数时可省略)
16
+ # 3. 块内其余行为任务描述(支持多行)
17
+
18
+ <!-- CLAWT-TASKS:START -->
19
+ # branch: feat-example-1
20
+ 在这里写第一个任务的描述
21
+ <!-- CLAWT-TASKS:END -->
22
+
23
+ <!-- CLAWT-TASKS:START -->
24
+ # branch: feat-example-2
25
+ 在这里写第二个任务的描述
26
+ 支持多行描述
27
+ <!-- CLAWT-TASKS:END -->
28
+ `;
package/src/index.ts CHANGED
@@ -21,6 +21,7 @@ import { registerProjectsCommand } from './commands/projects.js';
21
21
  import { registerCompletionCommand } from './commands/completion.js';
22
22
  import { registerInitCommand } from './commands/init.js';
23
23
  import { registerHomeCommand } from './commands/home.js';
24
+ import { registerTasksCommand } from './commands/tasks.js';
24
25
 
25
26
  // 从 package.json 读取版本号,避免硬编码
26
27
  const require = createRequire(import.meta.url);
@@ -62,6 +63,7 @@ registerProjectsCommand(program);
62
63
  registerCompletionCommand(program);
63
64
  registerInitCommand(program);
64
65
  registerHomeCommand(program);
66
+ registerTasksCommand(program);
65
67
 
66
68
  // 加载配置并应用命令别名
67
69
  const config = loadConfig();
@@ -85,3 +85,9 @@ export interface InitOptions {
85
85
  /** 指定主工作分支名(可选,默认使用当前分支) */
86
86
  branch?: string;
87
87
  }
88
+
89
+ /** tasks init 命令选项 */
90
+ export interface TasksInitOptions {
91
+ /** 输出文件路径(可选,默认 tasks.md) */
92
+ path?: string;
93
+ }
@@ -1,5 +1,5 @@
1
1
  export type { ClawtConfig, ConfigItemDefinition, ConfigDefinitions } from './config.js';
2
- export type { CreateOptions, RunOptions, ValidateOptions, MergeOptions, RemoveOptions, ResumeOptions, SyncOptions, ListOptions, StatusOptions, ProjectsOptions, InitOptions } from './command.js';
2
+ export type { CreateOptions, RunOptions, ValidateOptions, MergeOptions, RemoveOptions, ResumeOptions, SyncOptions, ListOptions, StatusOptions, ProjectsOptions, InitOptions, TasksInitOptions } from './command.js';
3
3
  export type { WorktreeInfo, WorktreeStatus } from './worktree.js';
4
4
  export type { ClaudeCodeResult } from './claudeCode.js';
5
5
  export type { TaskResult, TaskSummary } from './taskResult.js';
@@ -230,3 +230,22 @@ export function formatLocalISOString(date: Date): string {
230
230
 
231
231
  return `${iso}${sign}${hours}:${minutes}`;
232
232
  }
233
+
234
+ /**
235
+ * 生成任务模板文件名,格式:clawt-tasks-YYYY-MM-DD-HH-mm-ss.md
236
+ * @param {string} prefix - 文件名前缀
237
+ * @returns {string} 带时间戳的文件名
238
+ */
239
+ export function generateTaskFilename(prefix: string): string {
240
+ const now = new Date();
241
+ const pad = (n: number) => String(n).padStart(2, '0');
242
+ const timestamp = [
243
+ now.getFullYear(),
244
+ pad(now.getMonth() + 1),
245
+ pad(now.getDate()),
246
+ pad(now.getHours()),
247
+ pad(now.getMinutes()),
248
+ pad(now.getSeconds()),
249
+ ].join('-');
250
+ return `${prefix}-${timestamp}.md`;
251
+ }