clawt 2.19.0 → 3.0.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 (75) hide show
  1. package/README.md +12 -2
  2. package/dist/index.js +626 -219
  3. package/dist/postinstall.js +39 -6
  4. package/docs/alias.md +108 -0
  5. package/docs/completion.md +55 -0
  6. package/docs/config-file.md +43 -0
  7. package/docs/config.md +91 -0
  8. package/docs/create.md +85 -0
  9. package/docs/init.md +65 -0
  10. package/docs/list.md +67 -0
  11. package/docs/log.md +67 -0
  12. package/docs/merge.md +137 -0
  13. package/docs/notification.md +94 -0
  14. package/docs/projects.md +135 -0
  15. package/docs/remove.md +79 -0
  16. package/docs/reset.md +35 -0
  17. package/docs/resume.md +99 -0
  18. package/docs/run.md +146 -0
  19. package/docs/spec.md +156 -1879
  20. package/docs/status.md +155 -0
  21. package/docs/sync.md +114 -0
  22. package/docs/update-check.md +95 -0
  23. package/docs/validate.md +368 -0
  24. package/package.json +1 -1
  25. package/src/commands/alias.ts +1 -1
  26. package/src/commands/create.ts +10 -5
  27. package/src/commands/init.ts +75 -0
  28. package/src/commands/list.ts +1 -1
  29. package/src/commands/merge.ts +11 -4
  30. package/src/commands/remove.ts +10 -3
  31. package/src/commands/reset.ts +3 -0
  32. package/src/commands/resume.ts +9 -3
  33. package/src/commands/run.ts +9 -3
  34. package/src/commands/status.ts +1 -1
  35. package/src/commands/sync.ts +18 -6
  36. package/src/commands/validate.ts +46 -52
  37. package/src/constants/branch.ts +3 -0
  38. package/src/constants/config.ts +1 -1
  39. package/src/constants/index.ts +3 -3
  40. package/src/constants/messages/completion.ts +1 -1
  41. package/src/constants/messages/create.ts +3 -0
  42. package/src/constants/messages/index.ts +2 -0
  43. package/src/constants/messages/init.ts +18 -0
  44. package/src/constants/messages/remove.ts +2 -0
  45. package/src/constants/messages/sync.ts +3 -0
  46. package/src/constants/messages/validate.ts +6 -0
  47. package/src/constants/paths.ts +3 -0
  48. package/src/constants/prompt.ts +28 -0
  49. package/src/index.ts +2 -0
  50. package/src/types/command.ts +7 -1
  51. package/src/types/index.ts +2 -1
  52. package/src/types/projectConfig.ts +5 -0
  53. package/src/utils/config.ts +2 -1
  54. package/src/utils/git.ts +18 -0
  55. package/src/utils/index.ts +6 -1
  56. package/src/utils/json.ts +67 -0
  57. package/src/utils/project-config.ts +77 -0
  58. package/src/utils/validate-branch.ts +166 -0
  59. package/src/utils/worktree-matcher.ts +268 -1
  60. package/src/utils/worktree.ts +6 -2
  61. package/tests/unit/commands/create.test.ts +20 -16
  62. package/tests/unit/commands/init.test.ts +146 -0
  63. package/tests/unit/commands/merge.test.ts +7 -1
  64. package/tests/unit/commands/remove.test.ts +4 -0
  65. package/tests/unit/commands/reset.test.ts +2 -0
  66. package/tests/unit/commands/resume.test.ts +29 -8
  67. package/tests/unit/commands/run.test.ts +2 -0
  68. package/tests/unit/commands/sync.test.ts +6 -0
  69. package/tests/unit/commands/validate.test.ts +13 -0
  70. package/tests/unit/utils/config.test.ts +2 -2
  71. package/tests/unit/utils/project-config.test.ts +136 -0
  72. package/tests/unit/utils/update-checker.test.ts +28 -7
  73. package/tests/unit/utils/validate-branch.test.ts +272 -0
  74. package/tests/unit/utils/worktree-matcher.test.ts +142 -1
  75. package/tests/unit/utils/worktree.test.ts +6 -0
package/docs/log.md ADDED
@@ -0,0 +1,67 @@
1
+ ### 5.9 日志系统
2
+
3
+ **日志目录:** `~/.clawt/logs/`
4
+
5
+ **日志文件命名:** `clawt-YYYY-MM-DD.log`(按日期滚动)
6
+
7
+ **日志级别:**
8
+
9
+ | 级别 | 说明 | 使用场景 |
10
+ | ------- | ------------------ | -------------------------------------- |
11
+ | `debug` | 调试信息 | Git 命令执行详情、变量值等 |
12
+ | `info` | 一般信息 | 操作开始/完成、创建/移除 worktree 等 |
13
+ | `warn` | 警告信息 | 分支名被转换、非致命异常等 |
14
+ | `error` | 错误信息 | 命令执行失败、校验不通过等 |
15
+
16
+ **实现方案:** 使用 `winston` + `winston-daily-rotate-file` 库。
17
+
18
+ **日志格式:**
19
+
20
+ ```
21
+ [2025-02-06 14:30:22] [INFO ] 创建 worktree: ~/.clawt/worktrees/main-project/feature-scheme-1
22
+ [2025-02-06 14:30:22] [DEBUG] 执行命令: git worktree add -b feature-scheme-1 ~/.clawt/worktrees/main-project/feature-scheme-1
23
+ [2025-02-06 14:30:23] [WARN ] 分支名已转换: feature/a.b → feature-a-b
24
+ [2025-02-06 14:30:25] [ERROR] 分支 feature-scheme-1 已存在,无法创建
25
+ ```
26
+
27
+ **保留策略:**
28
+
29
+ - 日志文件保留 30 天
30
+ - 单个日志文件最大 10MB
31
+
32
+ #### `--debug` 控制台调试输出
33
+
34
+ 通过全局选项 `--debug` 可将调试日志实时输出到终端,方便排查问题。
35
+
36
+ **实现机制:**
37
+
38
+ - 在 Commander.js 的 `preAction` 钩子中检测 `--debug` 选项,按需调用 `enableConsoleTransport()` 函数
39
+ - `enableConsoleTransport()` 动态向 winston 实例添加 `Console` transport(level 为 `debug`),该函数幂等,多次调用不会重复添加 transport
40
+ - 相关常量定义在 `src/constants/logger.ts`:
41
+ - `DEBUG_TIMESTAMP_FORMAT`:时间戳格式(`HH:mm:ss.SSS`,精简,不含日期)
42
+
43
+ **控制台日志格式:**
44
+
45
+ ```
46
+ HH:mm:ss.SSS LEVEL 消息内容
47
+ ```
48
+
49
+ **日志级别颜色映射:**
50
+
51
+ | 级别 | 颜色 |
52
+ | ------- | ------ |
53
+ | `error` | 红色 |
54
+ | `warn` | 黄色 |
55
+ | `info` | 青色 |
56
+ | `debug` | 灰色 |
57
+
58
+ **使用示例:**
59
+
60
+ ```bash
61
+ clawt run -b feature-login --debug
62
+ clawt validate -b feature-scheme --debug
63
+ ```
64
+
65
+ > **注意:** `--debug` 选项不影响文件日志(file transport),文件日志始终按原有策略写入。控制台输出仅在传入 `--debug` 时启用。
66
+
67
+ ---
package/docs/merge.md ADDED
@@ -0,0 +1,137 @@
1
+ ### 5.6 合并验证过的分支
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ # 指定分支名(支持模糊匹配)
7
+ clawt merge -b <branchName> [-m <commitMessage>]
8
+
9
+ # 不指定分支名(列出所有分支供选择)
10
+ clawt merge [-m <commitMessage>]
11
+ ```
12
+
13
+ **参数:**
14
+
15
+ | 参数 | 必填 | 说明 |
16
+ | ---- | ---- | ------------------------------------------------------------------------ |
17
+ | `-b` | 否 | 要合并的分支名(支持模糊匹配,不传则列出所有分支供选择) |
18
+ | `-m` | 否 | 提交信息(目标 worktree 工作区有修改时必填) |
19
+
20
+ **运行流程:**
21
+
22
+ 1. **主 worktree 校验** (2.1)
23
+ 2. **确保在主工作分支上**(`ensureOnMainWorkBranch`):确保当前处于主工作分支上。如果在验证分支上,清理工作区后切回;如果在其他分支上,检查工作区是否干净,不干净则交互处理(reset/stash/exit),然后切回主工作分支。
24
+ 3. **解析目标 worktree**:根据 `-b` 参数解析目标 worktree,匹配策略如下:
25
+ - **未传 `-b` 参数**:
26
+ - 获取当前项目所有 worktree
27
+ - 无可用 worktree → 报错退出
28
+ - 仅 1 个 worktree → 直接使用,无需选择
29
+ - 多个 worktree → 通过交互式列表(Enquirer.Select)让用户选择
30
+ - **传了 `-b` 参数**:
31
+ 1. **精确匹配优先**:在 worktree 列表中查找分支名完全相同的 worktree,找到则直接使用
32
+ 2. **模糊匹配**(子串匹配,大小写不敏感):
33
+ - 唯一匹配 → 直接使用
34
+ - 多个匹配 → 通过交互式列表让用户从匹配结果中选择
35
+ 3. **无匹配** → 报错退出,并列出所有可用分支名
36
+ 3. **主 worktree 状态检测**
37
+ - 执行 `git status --porcelain`
38
+ - 如果有更改:
39
+ - 如果存在该分支的 validate 快照(`~/.clawt/validate-snapshots/<project>/<branchName>.tree`),额外输出警告 `主 worktree 可能存在 validate 残留状态,可先执行 clawt validate -b <branchName> --clean 清理`
40
+ - 提示 `主 worktree 有未提交的更改,请先处理`,退出
41
+ - 无更改 → 继续
42
+ 4. **Squash 检测与执行(auto-save 临时提交压缩)**
43
+ - 通过 `git log HEAD..<branchName> --format=%s` 检查目标分支是否存在以 `AUTO_SAVE_COMMIT_MESSAGE`(`chore: auto-save before sync`)为前缀的 commit
44
+ - **不存在** → 跳过,进入步骤 5
45
+ - **存在** → 提示用户是否将所有提交压缩为一个:
46
+ ```
47
+ 检测到 sync 产生的临时提交,是否将所有提交压缩为一个?
48
+ 压缩后变更将保留在目标worktree的暂存区,需要重新提交(可使用 Claude Code Cli或其他工具生成提交信息)
49
+ ```
50
+ - **用户选择不压缩** → 跳过,进入步骤 5
51
+ - **用户选择压缩** →
52
+ 1. 获取主分支名(从项目级配置 `clawtMainWorkBranch` 获取)
53
+ 2. 计算分叉点:`git merge-base <mainBranch> <branchName>`
54
+ 3. 在目标 worktree 中执行 `git reset --soft <merge-base>`,将所有 commit 撤销到暂存区
55
+ 4. 如果用户提供了 `-m` → 直接在目标 worktree 执行 `git commit -m '<commitMessage>'`,输出成功提示,继续步骤 5
56
+ 5. 如果用户未提供 `-m` → 提示用户前往目标 worktree 自行提交后重新执行 `clawt merge`:
57
+ ```
58
+ ✓ 已将所有提交压缩到暂存区
59
+ 请在目标 worktree 中提交后重新执行 merge:
60
+ cd <worktreePath>
61
+ 提交完成后执行:clawt merge -b <branch>
62
+ ```
63
+ **退出流程**
64
+ 5. **根据目标 worktree 状态决定是否需要提交**
65
+ - 检测目标 worktree 工作区是否干净(`git status --porcelain`)
66
+ - **工作区有未提交修改**:
67
+ - 如果用户未提供 `-m`,提示 `<worktreePath> 有未提交的修改,请通过 -m 参数提供提交信息`(其中 `<worktreePath>` 为目标 worktree 的完整路径),退出
68
+ - 提供了 `-m` → 执行提交:
69
+ ```bash
70
+ cd ~/.clawt/worktrees/<project>/<branchName>
71
+ git add .
72
+ git commit -m '<commitMessage>'
73
+ ```
74
+ - **工作区干净**:
75
+ - 检查目标分支相对于主分支是否有本地提交(`git log HEAD..<branchName> --oneline`)
76
+ - 有本地提交 → 跳过提交步骤,直接进入合并
77
+ - 无本地提交 → 提示 `目标 worktree 没有任何可合并的变更(工作区干净且无本地提交)`,退出
78
+ 6. **回到主 worktree 进行合并**
79
+ ```bash
80
+ cd <主 worktree 路径>
81
+ git merge <branchName>
82
+ ```
83
+ 7. **冲突检测**(双层机制)
84
+ - **try-catch 层**:`git merge` 抛出异常时,先检查是否有冲突,有冲突则报错退出,否则重新抛出原始异常
85
+ - **二次确认层**:即使 merge 未抛异常,也再次检查是否有合并冲突
86
+ - **有冲突** → 提示 `合并存在冲突,请手动处理:\n 解决冲突后执行 git add . && git merge --continue`,退出
87
+ - **无冲突** → 继续
88
+ 8. **推送(受 `autoPullPush` 配置控制)**
89
+ - `autoPullPush` 为 `false` → 输出提示 `已跳过自动 pull/push,请手动执行 git pull && git push`
90
+ - `autoPullPush` 为 `true` → 执行 `git pull` + `git push`:
91
+ - `git pull` 失败且有冲突 → 输出警告 `自动 pull 时发生冲突,merge 已完成但远程同步失败`,退出(不再 push)
92
+ - `git pull` 失败且无冲突 → 抛出错误
93
+ - `git push` 失败 → 输出警告 `自动 push 失败,merge 和 pull 已完成\n 请手动执行 git push`,退出
94
+ 9. **输出成功提示**
95
+
96
+ ```
97
+ # 提供了 -m 且已推送时
98
+ ✓ 分支 feature-scheme-1 已成功合并到当前分支
99
+ 提交信息: <commitMessage>
100
+ 已推送到远程仓库
101
+
102
+ # 提供了 -m 但未推送时
103
+ ✓ 分支 feature-scheme-1 已成功合并到当前分支
104
+ 提交信息: <commitMessage>
105
+
106
+ # 未提供 -m 且已推送时
107
+ ✓ 分支 feature-scheme-1 已成功合并到当前分支
108
+ 已推送到远程仓库
109
+
110
+ # 未提供 -m 且未推送时
111
+ ✓ 分支 feature-scheme-1 已成功合并到当前分支
112
+ ```
113
+
114
+ 10. **merge 成功后确认并清理 worktree 和分支(可选)**
115
+ - 如果配置文件中 `autoDeleteBranch` 为 `true`,自动执行清理
116
+ - 否则交互式询问用户是否清理
117
+ - 用户确认后,依次执行:
118
+ ```bash
119
+ # 移除 worktree
120
+ git worktree remove -f <worktree路径>
121
+ # 删除本地分支
122
+ git branch -D <branchName>
123
+ # 同步删除验证分支
124
+ git branch -D clawt-validate-<branchName>
125
+ # 修剪 worktree 引用
126
+ git worktree prune
127
+ # 如果项目 worktree 目录为空,则清理空目录
128
+ ```
129
+ - 输出清理成功提示:`✓ 已清理 worktree 和分支: <branchName>`
130
+ - 验证分支的删除时机与目标分支保持一致(见 [2.5 验证分支生命周期](#25-验证分支)):用户确认清理 → 同步删除验证分支;用户拒绝清理 → 验证分支也保留
131
+
132
+ 11. **清理 validate 快照**
133
+ - merge 成功后,如果存在该分支的 validate 快照(`~/.clawt/validate-snapshots/<project>/<branchName>.tree` 和 `<branchName>.head`),自动删除这些快照文件(merge 成功后快照已无意义)
134
+
135
+ > **注意:** 清理确认和清理操作均在 merge 成功后执行。只有 merge 成功才会询问用户是否清理 worktree 和分支,避免 merge 冲突时用户被提前询问造成困惑。
136
+
137
+ ---
@@ -0,0 +1,94 @@
1
+ ### 5.3 任务完成通知机制
2
+
3
+ **触发条件:** 通过 `clawt run` 启动了多个 Claude Code 任务后自动进入通知模式。
4
+
5
+ **机制说明:**
6
+
7
+ Claude Code CLI 以 `--output-format stream-json --verbose` 运行时,stdout 会持续输出 JSON 行(每行一个事件),包括 `system`、`assistant`(含 `tool_use` 和 `text`)、`user`(含 `tool_result`)等类型。任务结束时输出 `type: "result"` 事件:
8
+
9
+ ```json
10
+ {
11
+ "type": "result",
12
+ "subtype": "success",
13
+ "is_error": false,
14
+ "duration_ms": 182809,
15
+ "duration_api_ms": 0,
16
+ "num_turns": 1,
17
+ "result": "xxx",
18
+ "stop_reason": "stop_sequence",
19
+ "session_id": "e771e449-b695-48e7-8006-bbf3f0dd3e98",
20
+ "total_cost_usd": 0,
21
+ "usage": { ... }
22
+ }
23
+ ```
24
+
25
+ **流式事件解析(`src/utils/stream-parser.ts`):**
26
+
27
+ 由于 stdout 的 `data` 事件可能在行中间切割,使用 `createLineBuffer()` 行缓冲器拼接完整行后,通过 `parseStreamLine()` 解析为 `StreamEvent` 对象,再由 `parseStreamEvent()` 提取活动信息(`ParsedActivity`):
28
+
29
+ - **`tool_use` 类型**:提取工具名和文件路径/命令参数,格式如 `Read index.ts`、`Bash ls -la`
30
+ - **`text` 类型**:提取文本片段,格式如 `思考中: 让我分析一下`
31
+ - **`result` 类型**:构造 `ClaudeCodeResult` 对象,提取耗时、费用、结果文本等
32
+
33
+ 活动描述文本最大长度为 `ACTIVITY_TEXT_MAX_LENGTH`(30 字符),超出后截断并追加省略号。结果预览文本最大长度为 `RESULT_PREVIEW_MAX_LENGTH`(40 字符)。
34
+
35
+ **事件监听与通知流程:**
36
+
37
+ 1. 为每个 Claude Code 子进程维护状态(运行中 / 已完成 / 已失败)
38
+ 2. 监听每个子进程的 `close` 事件(基于 Node.js `ChildProcess` 的事件驱动机制)
39
+ 3. 在流式传输过程中实时解析每一行事件,当遇到 `type=result` 时保存到 `finalResult`;子进程触发 `close` 事件时,flush 行缓冲器并组装最终结果
40
+ 4. 在主 worktree 的 clawt 终端实时输出通知。TTY 环境下使用进度面板,进度面板每个任务行第二列显示 worktree 路径(终端可点击跳转),运行中显示实时活动描述,完成/失败后显示结果预览。任务行格式示例:
41
+
42
+ ```
43
+ [1/3] /path/to/worktree ⠹ 运行中 1m23s Read index.ts
44
+ [2/3] /path/to/worktree ✓ 完成 2m05s $0.08 任务已成功完成
45
+ [3/3] /path/to/worktree ◦ 排队中
46
+ ```
47
+
48
+ 5. 先完成的先通知,**不需要**失败重试机制
49
+ 6. 当所有任务完成后,输出汇总信息:
50
+
51
+ ```
52
+ ════════════════════════════════════════
53
+ 全部任务已完成 (3/3)
54
+ 成功: 2
55
+ 失败: 1
56
+ 总耗时: 245.3s
57
+ 总花费: $0.15
58
+ ════════════════════════════════════════
59
+ ```
60
+
61
+ #### 进度面板渲染机制
62
+
63
+ 进度面板由 `ProgressRenderer`(`src/utils/progress.ts`)负责渲染,渲染函数拆分到 `src/utils/progress-render.ts`。
64
+
65
+ **TTY 模式渲染策略(备选屏幕缓冲区):**
66
+
67
+ - **进入备选屏幕**:`start()` 时通过 `ALT_SCREEN_ENTER`(`\x1B[?1049h`)进入终端备选屏幕缓冲区,隔离进度面板与主屏幕内容
68
+ - **禁用行换行**:通过 `LINE_WRAP_DISABLE`(`\x1B[?7l`)防止超长行自动折行,配合按终端宽度截断保证每行只占一行
69
+ - **每帧渲染**:使用 `CLEAR_SCREEN` + `CURSOR_HOME` 清屏后完全重绘,无需计算 `CURSOR_UP` 回退量,不受终端 reflow 影响
70
+ - **防闪烁**:每帧渲染使用 Synchronized Output(`SYNC_OUTPUT_START` / `SYNC_OUTPUT_END`),终端缓冲全部输出后一次性刷新
71
+ - **行宽截断**:通过 `truncateToTerminalWidth()`(`src/utils/progress-render.ts`)将含 ANSI 转义码的字符串截断到终端可见列数,使用 `string-width` 库正确计算中文/emoji 宽度
72
+ - **终端 resize 响应**:监听 `process.stdout` 的 `resize` 事件,窗口宽度变化时立即触发重绘
73
+ - **退出时恢复**:`stop()` 时恢复行换行、显示光标、退出备选屏幕,然后在主屏幕上重新输出最终面板状态(备选屏幕内容不保留)
74
+ - **异常退出兜底**:注册 `process.on('exit')` 处理器,确保即使异常退出也能恢复终端状态
75
+
76
+ **任务行格式:**
77
+
78
+ ```
79
+ [1/3] /path/to/worktree ⠹ 运行中 1m23s Read index.ts
80
+ [2/3] /path/to/worktree ✓ 完成 2m05s $0.08 任务已成功完成
81
+ [3/3] /path/to/worktree ◦ 排队中
82
+ ```
83
+
84
+ - 第二列为 worktree 路径(`path.padEnd(maxPathWidth)` 对齐)
85
+ - 运行中状态:末尾显示实时活动描述文本(如工具名+文件名、思考中+文本片段)
86
+ - 完成/失败状态:末尾显示结果预览文本(从 `ClaudeCodeResult.result` 提取,最大 40 字符)
87
+
88
+ **非 TTY 降级模式:**
89
+
90
+ - 启动时输出 `[1/3] branch 启动 path`
91
+ - 完成时输出 `[1/3] branch ✓ 完成 duration cost detail`(`detail` 优先使用结果预览,无则回退到路径)
92
+ - 失败时输出 `[1/3] branch ✗ 失败 duration detail`
93
+
94
+ ---
@@ -0,0 +1,135 @@
1
+ ### 5.18 跨项目 Worktree 概览
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ clawt projects [name] [--json]
7
+ ```
8
+
9
+ **参数:**
10
+
11
+ | 参数 | 必填 | 说明 |
12
+ | -------- | ---- | ---------------------------------------------- |
13
+ | `[name]` | 否 | 指定项目名,查看该项目的 worktree 详情 |
14
+ | `--json` | 否 | 以 JSON 格式输出完整数据 |
15
+
16
+ **使用场景:**
17
+
18
+ 当使用 clawt 管理多个不同项目时,快速了解所有项目的 worktree 数量、磁盘占用和最近活跃时间。也可以指定项目名查看该项目下每个 worktree 的分支、路径、最后修改时间和磁盘占用。
19
+
20
+ **注意:** `projects` 命令不需要在主 worktree 中执行(与其他命令不同),它直接扫描 `~/.clawt/worktrees/` 目录。
21
+
22
+ **运行流程:**
23
+
24
+ #### 无参数模式(项目概览)
25
+
26
+ 1. 扫描 `~/.clawt/worktrees/` 目录,列出所有项目子目录(如果目录不存在或为空,输出 `(暂无项目,worktrees 目录为空)` 提示并结束)
27
+ 2. 对每个项目收集以下信息:
28
+ - **项目名**(目录名即项目名)
29
+ - **worktree 数量**(项目目录下的子目录数)
30
+ - **最近活跃时间**(取项目目录自身和所有 worktree 目录 mtime 的最大值,通过 `formatLocalISOString()` 格式化为本机时区的 ISO 8601 字符串)
31
+ - **磁盘占用**(通过 `calculateDirSize()` 递归计算整个项目目录的总大小)
32
+ 3. 按最近活跃时间降序排序
33
+ 4. 输出概览信息(文本或 JSON)
34
+
35
+ #### 指定项目模式(worktree 详情)
36
+
37
+ 1. 检查 `~/.clawt/worktrees/<name>/` 是否存在,不存在则报错退出
38
+ 2. 扫描项目目录,对每个 worktree 子目录收集(如果项目目录下无 worktree 子目录,输出 `(该项目下无 worktree)` 提示并结束):
39
+ - **分支名**(目录名即分支名)
40
+ - **worktree 路径**
41
+ - **最后修改时间**(目录 mtime,通过 `formatLocalISOString()` 格式化)
42
+ - **磁盘占用**(通过 `calculateDirSize()` 递归计算)
43
+ 3. 按最后修改时间降序排序
44
+ 4. 输出详情信息(文本或 JSON)
45
+
46
+ **文本输出格式(概览模式):**
47
+
48
+ ```
49
+ ════════════════════════════════════════
50
+ 项目概览
51
+ ════════════════════════════════════════
52
+
53
+ ● my-project
54
+ 3 个 worktree 最近活跃: 2 小时前 磁盘占用: 1.5 GB
55
+
56
+ ● another-project
57
+ 1 个 worktree 最近活跃: 3 天前 磁盘占用: 256.0 MB
58
+
59
+ ────────────────────────────────────────
60
+
61
+ 共 2 个项目 总占用: 1.8 GB
62
+
63
+ ════════════════════════════════════════
64
+ ```
65
+
66
+ **文本输出格式(详情模式):**
67
+
68
+ ```
69
+ ════════════════════════════════════════
70
+ 项目详情: my-project
71
+ ════════════════════════════════════════
72
+
73
+ ◆ 路径: ~/.clawt/worktrees/my-project
74
+ 总占用: 1.5 GB
75
+
76
+ ────────────────────────────────────────
77
+
78
+ ● feature-login
79
+ ~/.clawt/worktrees/my-project/feature-login
80
+ 最后修改: 2 小时前 磁盘占用: 800.0 MB
81
+
82
+ ● feature-signup
83
+ ~/.clawt/worktrees/my-project/feature-signup
84
+ 最后修改: 1 天前 磁盘占用: 700.0 MB
85
+
86
+ ════════════════════════════════════════
87
+ ```
88
+
89
+ **JSON 输出格式(概览模式,`--json`):**
90
+
91
+ ```json
92
+ {
93
+ "projects": [
94
+ {
95
+ "name": "my-project",
96
+ "worktreeCount": 3,
97
+ "lastActiveTime": "2025-06-15T18:30:00.000+08:00",
98
+ "diskUsage": 1610612736
99
+ }
100
+ ],
101
+ "totalProjects": 1,
102
+ "totalDiskUsage": 1610612736
103
+ }
104
+ ```
105
+
106
+ **JSON 输出格式(详情模式,`--json`):**
107
+
108
+ ```json
109
+ {
110
+ "name": "my-project",
111
+ "projectDir": "/Users/<username>/.clawt/worktrees/my-project",
112
+ "worktrees": [
113
+ {
114
+ "branch": "feature-login",
115
+ "path": "/Users/<username>/.clawt/worktrees/my-project/feature-login",
116
+ "lastModifiedTime": "2025-06-15T18:30:00.000+08:00",
117
+ "diskUsage": 838860800
118
+ }
119
+ ],
120
+ "totalDiskUsage": 838860800
121
+ }
122
+ ```
123
+
124
+ **实现要点:**
125
+
126
+ - 命令注册函数:`registerProjectsCommand()`(在 `src/commands/projects.ts`)
127
+ - 类型定义在 `src/types/project.ts`:`ProjectOverview`、`ProjectWorktreeDetail`、`ProjectDetailResult`、`ProjectsOverviewResult`
128
+ - 命令选项类型:`ProjectsOptions`(在 `src/types/command.ts`)
129
+ - 消息常量在 `PROJECTS_MESSAGES`(在 `src/constants/messages/projects.ts`)
130
+ - 时间格式化使用 `formatLocalISOString()`(在 `src/utils/formatter.ts`),输出本机时区的 ISO 8601 字符串(替代 `Date.toISOString()` 的 UTC 输出)
131
+ - 磁盘大小展示使用 `formatDiskSize()`(在 `src/utils/formatter.ts`),将字节数格式化为带单位的可读字符串
132
+ - 目录大小计算使用 `calculateDirSize()`(在 `src/utils/fs.ts`),递归遍历目录计算总字节数
133
+ - 时间的相对展示使用 `formatRelativeTime()`(在 `src/utils/formatter.ts`),将 ISO 8601 日期转换为中文相对时间(如"2 小时前")
134
+
135
+ ---
package/docs/remove.md ADDED
@@ -0,0 +1,79 @@
1
+ ### 5.5 移除 Worktree
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ # 移除当前项目所有 worktree
7
+ clawt remove --all
8
+
9
+ # 指定分支名(支持模糊匹配)
10
+ clawt remove -b <branchName>
11
+
12
+ # 不指定参数(列出所有分支供多选)
13
+ clawt remove
14
+ ```
15
+
16
+ **参数:**
17
+
18
+ | 参数 | 必填 | 说明 |
19
+ | --------- | ---- | ---------------------------------------------------------------------- |
20
+ | `--all` | 否 | 移除当前项目 (`~/.clawt/worktrees/<project>/`) 下所有 worktree |
21
+ | `-b` | 否 | 指定分支名(支持模糊匹配,不传则列出所有分支供多选) |
22
+
23
+ > **提示:** 不传 `--all` 也不传 `-b` 时,会列出当前项目所有 worktree 供交互式多选。
24
+
25
+ **运行流程:**
26
+
27
+ 1. **主 worktree 校验** (2.1)
28
+ 2. **获取项目名** (2.2)
29
+ 3. **确定待移除的 worktree 列表**:
30
+ - **指定 `--all`** → 选中当前项目所有 worktree(若当前项目无 worktree,则提示并退出)
31
+ - **未指定 `--all`** → 通过 `resolveTargetWorktrees` 解析目标 worktree(多选版本),匹配策略如下:
32
+ - **未传 `-b` 参数**:
33
+ - 无可用 worktree → 报错退出
34
+ - 仅 1 个 worktree → 直接使用,无需选择
35
+ - 多个 worktree → 通过交互式多选列表(Enquirer.MultiSelect)让用户选择(空格选择,回车确认)
36
+ - **传了 `-b` 参数**:
37
+ 1. **精确匹配优先**:在 worktree 列表中查找分支名完全相同的 worktree,找到则直接使用
38
+ 2. **模糊匹配**(子串匹配,大小写不敏感):
39
+ - 唯一匹配 → 直接使用
40
+ - 多个匹配 → 通过交互式多选列表让用户从匹配结果中选择
41
+ 3. **无匹配** → 报错退出,并列出所有可用分支名
42
+ 4. 列出即将移除的 worktree 及对应分支:
43
+
44
+ ```
45
+ 即将移除以下 worktree 及本地分支:
46
+
47
+ 1. ~/.clawt/worktrees/main-project/feature-scheme-1 → 分支: feature-scheme-1 验证分支: clawt-validate-feature-scheme-1
48
+ 2. ~/.clawt/worktrees/main-project/feature-scheme-2 → 分支: feature-scheme-2 验证分支: clawt-validate-feature-scheme-2
49
+ 3. ~/.clawt/worktrees/main-project/feature-scheme-3 → 分支: feature-scheme-3 验证分支: clawt-validate-feature-scheme-3
50
+
51
+ 是否同时删除对应的本地分支和验证分支?(y/N)
52
+ ```
53
+
54
+ 5. 用户确认后(只需确认一次),对每个 worktree 依次执行(单个失败不影响其他):
55
+
56
+ ```bash
57
+ # 确保当前处于主工作分支上(若不在则自动切回)
58
+ git checkout <clawtMainWorkBranch>
59
+
60
+ # 移除 worktree
61
+ git worktree remove -f <worktree路径>
62
+
63
+ # 如果用户选择了删除分支
64
+ git branch -D <branchName>
65
+
66
+ # 无条件删除验证分支和清理快照(不受用户确认控制)
67
+ git branch -D clawt-validate-<branchName>
68
+ # 清理该分支对应的 validate 快照
69
+ ```
70
+
71
+ 6. 如果配置文件 `~/.clawt/config.json` 中 `autoDeleteBranch` 为 `true`,则跳过询问,直接删除分支。
72
+
73
+ 7. 如果使用 `--all` 模式,额外清理整个项目的 validate 快照目录。
74
+
75
+ 8. 移除完成后,清理空目录(如果 `~/.clawt/worktrees/<project>/` 下已无 worktree,则删除该项目目录)。
76
+
77
+ 9. 批量移除时,单个 worktree 移除失败不会中断整个流程,而是收集所有失败项,最后汇总报告并以错误状态退出(抛出 ClawtError)。
78
+
79
+ ---
package/docs/reset.md ADDED
@@ -0,0 +1,35 @@
1
+ ### 5.13 重置主 Worktree 工作区和暂存区
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ clawt reset
7
+ ```
8
+
9
+ **无参数。**
10
+
11
+ **使用场景:**
12
+
13
+ 当用户通过 `clawt validate` 将分支变更迁移到主 worktree 后,希望快速清除工作区和暂存区的所有修改,恢复到干净状态。与 `clawt validate --clean` 的区别在于:`reset` 仅重置工作区和暂存区,**不删除** validate 快照文件,也**不切换分支**,适用于只想清空变更而保留快照以便后续增量 validate 的场景。
14
+
15
+ > **设计原因**:reset 的职责是「重置工作区状态」,分支切换属于 validate --clean 和 remove 等命令的职责。将分支切换耦合到 reset 会违反单一职责原则。
16
+
17
+ **运行流程:**
18
+
19
+ 1. **主 worktree 校验** (2.1)
20
+ 2. **项目级配置校验**(`requireProjectConfig()`,因 reset 不调用 `ensureOnMainWorkBranch`,需自行校验)
21
+ 3. **检测工作区状态**:通过 `git status --porcelain` 检测主 worktree 是否有未提交的更改
22
+ - **工作区干净** → 输出提示 `主 worktree 工作区和暂存区已是干净状态,无需重置`,退出
23
+ - **工作区不干净** → 继续
24
+ 3. **确认破坏性操作**:如果配置项 `confirmDestructiveOps` 为 `true`,提示确认(显示即将执行的危险指令和操作后果),用户取消则退出
25
+ 4. **重置工作区和暂存区**:
26
+ ```bash
27
+ git reset --hard HEAD
28
+ git clean -fd
29
+ ```
30
+ 5. **输出成功提示**:
31
+ ```
32
+ ✓ 主 worktree 工作区和暂存区已重置
33
+ ```
34
+
35
+ ---
package/docs/resume.md ADDED
@@ -0,0 +1,99 @@
1
+ ### 5.11 在已有 Worktree 中恢复会话
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ # 指定分支名(支持模糊匹配)
7
+ clawt resume -b <branchName>
8
+
9
+ # 不指定分支名(列出所有分支供多选)
10
+ clawt resume
11
+ ```
12
+
13
+ **参数:**
14
+
15
+ | 参数 | 必填 | 说明 |
16
+ | ---- | ---- | ----------------------------------------------------- |
17
+ | `-b` | 否 | 要恢复的分支名(支持模糊匹配,不传则列出所有分支供多选) |
18
+
19
+ **使用场景:**
20
+
21
+ 当用户之前通过 `clawt run` 或 `clawt create` 创建了 worktree 但会话已结束,希望在该 worktree 中重新打开 Claude Code 交互式界面继续工作。支持一次选中多个分支,自动在独立终端 Tab 中批量恢复。
22
+
23
+ **运行流程:**
24
+
25
+ 1. **主 worktree 校验** (2.1)
26
+ 2. **Claude Code CLI 校验**:确认 `claude` CLI 可用
27
+ 3. **解析目标 worktree**:根据是否传入 `-b` 参数以及 worktree 数量,采用不同的解析策略:
28
+ - **未传 `-b` 参数**:
29
+ - 获取当前项目所有 worktree
30
+ - 无可用 worktree → 报错退出
31
+ - 仅 1 个 worktree → 通过 `resolveTargetWorktrees` 直接使用,无需选择
32
+ - 多个 worktree → 通过 `promptGroupedMultiSelectBranches` 展示**按日期分组的交互式多选列表**(详见下文「按日期分组多选」)
33
+ - **传了 `-b` 参数**:通过 `resolveTargetWorktrees` 解析,匹配策略如下:
34
+ 1. **精确匹配优先**:在 worktree 列表中查找分支名完全相同的 worktree,找到则直接使用
35
+ 2. **模糊匹配**(子串匹配,大小写不敏感):
36
+ - 唯一匹配 → 直接使用
37
+ - 多个匹配 → 通过交互式多选列表让用户从匹配结果中选择
38
+ 3. **无匹配** → 报错退出,并列出所有可用分支名
39
+ 4. **根据选中数量自动分发**:
40
+ - **用户未选择任何分支** → 直接退出
41
+ - **选中 1 个** → 在当前终端恢复(同原有行为),通过 `launchInteractiveClaude()` 启动(使用 `spawnSync` + `inherit stdio`)
42
+ - **选中多个** → 进入批量恢复流程(见下文)
43
+
44
+ **批量恢复流程:**
45
+
46
+ 1. **计算会话状态**:一次性遍历所有选中的 worktree,通过 `hasClaudeSessionHistory()` 检测是否存在历史会话,构建 sessionMap 避免重复 I/O
47
+ 2. **输出预览**:列出即将恢复的分支及其会话状态("继续上次对话"或"新对话")
48
+ 3. **用户确认**:提示即将在 N 个独立终端 Tab 中恢复会话,等待用户确认
49
+ 4. **逐个在新终端 Tab 中启动**:通过 `launchInteractiveClaudeInNewTerminal()` 构建 shell 命令并通过 AppleScript 在新终端 Tab 中执行
50
+ 5. **输出完成提示**
51
+
52
+ **终端 Tab 管理:**
53
+
54
+ 批量恢复通过 `openCommandInNewTerminalTab()`(`src/utils/terminal.ts`)在新终端 Tab 中启动 Claude Code。终端类型由配置项 `terminalApp` 控制:
55
+
56
+ | 配置值 | 行为 |
57
+ | ---------- | ------------------------------------------------------------ |
58
+ | `auto` | 自动检测:优先检测 iTerm2 是否已安装(`/Applications/iTerm.app`),已安装则使用 iTerm2,否则降级到 Terminal.app |
59
+ | `iterm2` | 强制使用 iTerm2 |
60
+ | `terminal` | 强制使用 Terminal.app |
61
+
62
+ **平台限制:** 批量恢复目前仅支持 macOS 平台(通过 AppleScript 打开终端 Tab)。非 macOS 平台会抛出错误。
63
+
64
+ **权限要求:** Terminal.app 通过 System Events 模拟键盘操作(`Cmd+T`)新建 Tab,需要在「系统设置 → 隐私与安全性 → 辅助功能」中授权终端应用。iTerm2 使用原生 AppleScript 接口,无需辅助功能权限。
65
+
66
+ 启动命令通过配置项 `claudeCodeCommand`(默认值 `claude`)指定,与 `clawt run` 不传 `--tasks` 时的交互式界面行为一致。
67
+
68
+ **按日期分组多选:**
69
+
70
+ 当未传 `-b` 且有多个 worktree 时,使用 `promptGroupedMultiSelectBranches` 展示按创建日期分组的交互式多选列表,实现流程如下:
71
+
72
+ 1. **日期分组**(`groupWorktreesByDate`):通过 `statSync` 获取各 worktree 目录的文件系统创建时间(`birthtime`),按本地时区格式化为 `YYYY-MM-DD` 作为分组键。无法获取创建时间的分支归入「未知日期」组。分组按日期降序排列,未知日期组在最后。
73
+ 2. **构建选项列表**(`buildGroupedChoices`):生成包含以下元素的 Enquirer MultiSelect choices 数组:
74
+ - 顶部:全局全选选项 `[select-all]`
75
+ - 每组:日期分隔线(显示日期和相对时间,如「2026-02-26(昨天)」)→ 组级全选选项 `[select-all: YYYY-MM-DD]` → 该组内各分支
76
+ 2.5. **构建组成员映射**(`buildGroupMembershipMap`):生成"组全选 name → 该组分支 name 列表"的 Map,供三级联动的 `space()` 方法快速查找某个组全选项对应的所有分支
77
+ 3. **三级联动选择**:通过继承 Enquirer MultiSelect 并覆写 `space()` 方法实现,同步逻辑由 `syncGlobalSelectAll` 和 `syncGroupSelectAll` 两个内部函数负责:
78
+ - **全局全选**:toggle 所有 choices(含组全选)
79
+ - **组级全选**:toggle 该组内所有分支,并同步全局全选状态
80
+ - **普通分支**:toggle 该分支,同步所属组全选和全局全选状态
81
+ 4. **过滤结果**:返回时过滤掉全选项和组全选项,只返回实际选中的 worktree
82
+
83
+ 相对日期显示规则:`formatRelativeDate` 基于自然日差值计算——今天 / 昨天 / N 天前 / N 个月前 / N 年前。
84
+
85
+ 相关常量定义在 `src/constants/prompt.ts`:
86
+
87
+ | 常量 | 说明 |
88
+ | ---- | ---- |
89
+ | `GROUP_SELECT_ALL_PREFIX` | 组级全选选项的 name 前缀(`__group_select_all_`) |
90
+ | `GROUP_SELECT_ALL_LABEL(dateLabel)` | 生成组级全选选项的显示文本 |
91
+ | `GROUP_SEPARATOR_LABEL(dateLabel, relativeTime)` | 生成日期分隔线的显示文本(含 chalk 高亮) |
92
+ | `UNKNOWN_DATE_GROUP` | 无法获取创建日期时的默认分组名称(`未知日期`) |
93
+ | `UNKNOWN_DATE_SEPARATOR_LABEL` | 未知日期分组的分隔线显示文本 |
94
+ | `SELECT_ALL_NAME` | 全局全选选项的标识名称(`__select_all__`) |
95
+ | `SELECT_ALL_LABEL` | 全局全选选项的显示文本(`[select-all]`) |
96
+
97
+ **会话自动续接:** 启动前会自动检测该 worktree 是否存在 Claude Code 历史会话(通过检查 `~/.claude/projects/<encoded-path>/` 下是否有 `.jsonl` 文件判断),如果存在则自动追加 `--continue` 参数继续上次对话,否则打开新对话。启动信息中会显示当前模式("继续上次对话"或"新对话")。路径编码规则:将绝对路径中所有非字母数字字符替换为 `-`(与 Claude Code 源码的编码逻辑一致)。
98
+
99
+ ---