clawt 2.4.0 → 2.5.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/.claude/agent-memory/docs-sync-updater/MEMORY.md +13 -4
- package/CLAUDE.md +12 -9
- package/README.md +37 -9
- package/dist/index.js +87 -18
- package/docs/spec.md +84 -21
- package/package.json +1 -1
- package/src/commands/list.ts +39 -3
- package/src/commands/merge.ts +3 -5
- package/src/commands/resume.ts +89 -16
- package/src/commands/run.ts +8 -0
- package/src/constants/messages.ts +12 -0
- package/src/types/command.ts +8 -2
- package/src/types/index.ts +1 -1
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
- run 命令对应 `5.2 批量创建 Worktree + 执行 Claude Code 任务`,流程按步骤编号描述
|
|
9
9
|
- merge 命令对应 `5.6 合并验证过的分支`,流程按步骤编号描述
|
|
10
10
|
- config 命令对应 `5.10 查看全局配置`,只读展示配置
|
|
11
|
-
- resume 命令对应 `5.11 在已有 Worktree
|
|
11
|
+
- resume 命令对应 `5.11 在已有 Worktree 中恢复会话`,支持模糊匹配和交互式分支选择(-b 可选)
|
|
12
12
|
- 配置项说明在 `5.7 默认配置文件` 章节的表格中
|
|
13
13
|
- 更新模式:新增步骤时追加编号,配置项影响范围变化时更新说明列
|
|
14
14
|
|
|
15
15
|
### CLAUDE.md
|
|
16
16
|
- 面向 Claude Code 的项目架构指引,精简扼要
|
|
17
17
|
- run 命令流程在 `核心流程(run 命令)` 章节,编号列表描述
|
|
18
|
-
- resume 命令流程在独立的 `### resume 命令流程`
|
|
18
|
+
- resume 命令流程在独立的 `### resume 命令流程` 章节,编号列表 + 缩进列表描述匹配策略
|
|
19
19
|
- merge 和 run 中断清理在 `validate + merge 工作流` 章节,一行式描述用箭头连接流程
|
|
20
20
|
- utils 目录描述用括号内逗号分隔列举功能模块
|
|
21
21
|
- 更新模式:编号列表追加步骤,箭头链追加阶段,括号内追加关键词
|
|
@@ -28,14 +28,21 @@
|
|
|
28
28
|
|
|
29
29
|
## 关键约定
|
|
30
30
|
- `autoDeleteBranch` 配置项影响三处:remove 命令、merge 命令、run 中断清理
|
|
31
|
-
-
|
|
31
|
+
- `confirmDestructiveOps` 配置项影响两处:reset 命令、validate --clean
|
|
32
|
+
- merge 的清理确认和清理操作均在 merge 成功后执行(避免 merge 冲突时提前询问用户造成困惑)
|
|
32
33
|
- merge 成功后自动清理对应的 validate 快照(hasSnapshot + removeSnapshot)
|
|
34
|
+
- merge 成功消息根据 `autoPullPush` 配置动态显示推送状态
|
|
33
35
|
- run 的中断清理在所有子进程退出后执行
|
|
36
|
+
- run 交互式模式在创建 worktree 前检测分支是否已存在,已存在则提示使用 resume
|
|
37
|
+
- remove 命令删除 worktree 时自动清理对应快照,`--all` 模式额外清理项目快照目录
|
|
38
|
+
- remove 批量操作时收集错误继续处理,最后汇总报告
|
|
34
39
|
- 文档中文风格,技术术语保留英文(worktree, merge, branch, SIGINT 等)
|
|
35
40
|
- cleanupWorktrees 是 merge 和 run 共用的公共清理函数(在 src/utils/worktree.ts)
|
|
36
41
|
- `launchInteractiveClaude` 是 run(交互式模式)和 resume 共用的公共函数(在 src/utils/claude.ts)
|
|
37
42
|
- killAllChildProcesses 是 run 专用的子进程终止函数(在 src/utils/shell.ts)
|
|
38
|
-
- validate 快照管理函数在 `src/utils/validate-snapshot.ts`,被 validate 和
|
|
43
|
+
- validate 快照管理函数在 `src/utils/validate-snapshot.ts`,被 validate、merge 和 remove 三个命令使用
|
|
44
|
+
- `confirmDestructiveAction` 在 `src/utils/formatter.ts`,被 reset 和 validate --clean 使用
|
|
45
|
+
- sanitizeBranchName 清理后为空串时抛出 BRANCH_NAME_EMPTY 错误
|
|
39
46
|
|
|
40
47
|
## 配置项同步检查点
|
|
41
48
|
|
|
@@ -68,6 +75,8 @@ Notes:
|
|
|
68
75
|
- resume 和 run(交互式模式)共用 `launchInteractiveClaude()`,该函数从 run.ts 提取到 src/utils/claude.ts
|
|
69
76
|
- `claudeCodeCommand` 配置项同时影响 run 交互式模式和 resume 命令
|
|
70
77
|
- reset 命令与 validate --clean 的区别:reset 不删除快照文件,validate --clean 会删除快照
|
|
78
|
+
- resume 的 `-b` 参数为可选,核心函数 `resolveTargetWorktree()` 封装匹配策略:精确→模糊(子串,大小写不敏感)→交互选择
|
|
79
|
+
- resume 的交互式选择使用 Enquirer.Select(`promptSelectBranch()`),消息常量在 `MESSAGES.RESUME_*`
|
|
71
80
|
|
|
72
81
|
## validate 快照机制
|
|
73
82
|
|
package/CLAUDE.md
CHANGED
|
@@ -34,8 +34,9 @@ run 命令有两种模式:
|
|
|
34
34
|
|
|
35
35
|
1. `validateMainWorktree()` 确认在主 worktree 根目录
|
|
36
36
|
2. `validateClaudeCodeInstalled()` 确认 claude CLI 可用
|
|
37
|
-
3.
|
|
38
|
-
4. `
|
|
37
|
+
3. 检测分支是否已存在(`checkBranchExists()`),已存在则提示使用 `clawt resume -b <branch>` 恢复会话
|
|
38
|
+
4. `createWorktrees()` 创建单个 worktree
|
|
39
|
+
5. `launchInteractiveClaude()` 通过 `spawnSync` + `inherit stdio` 在 worktree 中直接启动 Claude Code 交互式界面(启动命令由配置项 `claudeCodeCommand` 指定,默认 `claude`)
|
|
39
40
|
|
|
40
41
|
**模式二:传 `--tasks`(并行任务模式)**
|
|
41
42
|
|
|
@@ -50,7 +51,9 @@ run 命令有两种模式:
|
|
|
50
51
|
|
|
51
52
|
1. `validateMainWorktree()` 确认在主 worktree 根目录
|
|
52
53
|
2. `validateClaudeCodeInstalled()` 确认 claude CLI 可用
|
|
53
|
-
3. `
|
|
54
|
+
3. `resolveTargetWorktree()` 解析目标 worktree(`-b` 可选):
|
|
55
|
+
- 未传 `-b`:仅 1 个 worktree 直接使用,多个通过 `promptSelectBranch()`(Enquirer.Select)交互选择
|
|
56
|
+
- 传了 `-b`:`findExactMatch()` 精确匹配 → `findFuzzyMatches()` 子串模糊匹配(大小写不敏感,唯一直接使用,多个交互选择) → 无匹配报错并列出可用分支
|
|
54
57
|
4. `launchInteractiveClaude()` 在目标 worktree 中启动 Claude Code 交互式界面
|
|
55
58
|
|
|
56
59
|
### validate + merge 工作流
|
|
@@ -58,12 +61,12 @@ run 命令有两种模式:
|
|
|
58
61
|
- `validate`:将目标分支的全量变更(已提交 + 未提交)通过 `git diff HEAD...branch --binary` 的 patch 方式迁移到主 worktree,便于在主 worktree 中测试。支持两种模式:
|
|
59
62
|
- **首次 validate**(无历史快照):patch 迁移全量变更 → 通过 `git write-tree` 保存快照为 git tree 对象 → 结果:暂存区=空,工作目录=全量变更
|
|
60
63
|
- **增量 validate**(存在历史快照):读取旧 tree hash → 确保主 worktree 干净 → patch 迁移最新变更 → 保存新 tree 对象快照 → `git read-tree` 将旧 tree 载入暂存区 → 结果:暂存区=上次快照,工作目录=最新变更(可通过 `git diff` 查看增量差异)
|
|
61
|
-
- `--clean`
|
|
64
|
+
- `--clean` 选项:根据 `confirmDestructiveOps` 配置提示确认 → 重置主 worktree + 删除对应快照文件
|
|
62
65
|
- 快照存储路径:`~/.clawt/validate-snapshots/<projectName>/<branchName>.tree`(存储 git tree 对象 hash)
|
|
63
66
|
- tree 对象不依赖主分支 HEAD,无需一致性校验
|
|
64
67
|
- 变更检测:同时检测目标 worktree 的未提交修改和已提交 commit,两者均无则提示无需验证
|
|
65
68
|
- 未提交修改处理:有未提交修改时先做临时 commit,diff 完成后通过 `git reset --soft` 撤销恢复原状
|
|
66
|
-
- `merge`:检测目标 worktree 状态(有修改则需 `-m` 提交,已提交则跳过,无变更则报错)→ **squash 检测**(检查目标分支是否存在 `AUTO_SAVE_COMMIT_MESSAGE` 前缀的 auto-save commit,如有则提示用户是否压缩所有提交:用户确认后通过 `gitMergeBase` 计算分叉点、`gitResetSoftTo` 将所有 commit reset 到暂存区;有 `-m` 则直接提交继续流程,无 `-m` 则提示用户自行提交后退出)→ 合并到主 worktree → pull
|
|
69
|
+
- `merge`:检测目标 worktree 状态(有修改则需 `-m` 提交,已提交则跳过,无变更则报错)→ **squash 检测**(检查目标分支是否存在 `AUTO_SAVE_COMMIT_MESSAGE` 前缀的 auto-save commit,如有则提示用户是否压缩所有提交:用户确认后通过 `gitMergeBase` 计算分叉点、`gitResetSoftTo` 将所有 commit reset 到暂存区;有 `-m` 则直接提交继续流程,无 `-m` 则提示用户自行提交后退出)→ 合并到主 worktree → 根据 `autoPullPush` 配置决定是否 pull + push(成功消息动态显示推送状态)→ merge 成功后确认并清理 worktree 和分支(受 `autoDeleteBranch` 配置或交互式确认控制)→ 清理对应的 validate 快照
|
|
67
70
|
- `run` 中断清理:Ctrl+C 终止所有子进程后,根据 `autoDeleteBranch` 配置自动清理或交互式确认清理本次创建的 worktree 和分支
|
|
68
71
|
|
|
69
72
|
### sync 命令流程
|
|
@@ -80,14 +83,14 @@ run 命令有两种模式:
|
|
|
80
83
|
|
|
81
84
|
1. `validateMainWorktree()` 确认在主 worktree 根目录
|
|
82
85
|
2. 检测主 worktree 工作区和暂存区是否干净(`isWorkingDirClean()`)
|
|
83
|
-
3. 不干净 → `gitResetHard()` + `gitCleanForce()` 重置工作区和暂存区(保留 validate 快照)
|
|
86
|
+
3. 不干净 → 根据 `confirmDestructiveOps` 配置决定是否通过 `confirmDestructiveAction()` 提示确认 → `gitResetHard()` + `gitCleanForce()` 重置工作区和暂存区(保留 validate 快照)
|
|
84
87
|
4. 已干净 → 提示无需重置
|
|
85
88
|
|
|
86
89
|
### 目录层级
|
|
87
90
|
|
|
88
91
|
- `src/commands/` — 各命令的注册与处理逻辑
|
|
89
|
-
- `src/utils/` — 工具函数(git 操作(含三点 diff、分支合并、冲突检测、merge-base 计算、commit message 检测、soft reset 到指定 commit、write-tree/read-tree
|
|
90
|
-
- `src/constants/` — 常量定义(路径、退出码、消息模板、分支规则、配置默认值、终端控制序列、validate 快照目录、sync 相关消息、git 常量(如 `AUTO_SAVE_COMMIT_MESSAGE`)、squash 相关消息、reset
|
|
92
|
+
- `src/utils/` — 工具函数(git 操作(含三点 diff、分支合并、冲突检测、merge-base 计算、commit message 检测、soft reset 到指定 commit、write-tree/read-tree、分支存在性检测等)、shell 执行与子进程管理、分支名处理、worktree 管理与批量清理、配置、格式化输出与破坏性操作确认、交互式输入、Claude Code 交互式启动、validate 快照管理(基于 git tree 对象))
|
|
93
|
+
- `src/constants/` — 常量定义(路径、退出码、消息模板、分支规则、配置默认值、终端控制序列、validate 快照目录、sync 相关消息、git 常量(如 `AUTO_SAVE_COMMIT_MESSAGE`)、squash 相关消息、reset 相关消息、remove 相关消息、破坏性操作确认消息)
|
|
91
94
|
- `src/types/` — TypeScript 类型定义
|
|
92
95
|
- `src/errors/` — 自定义 `ClawtError` 错误类(携带退出码)
|
|
93
96
|
- `src/logger/` — winston 日志(按日期滚动,写入 `~/.clawt/logs/`)
|
|
@@ -96,7 +99,7 @@ run 命令有两种模式:
|
|
|
96
99
|
|
|
97
100
|
- 所有命令执行前都会调用 `validateMainWorktree()` 确保在主 worktree 根目录(`git rev-parse --git-common-dir === ".git"`)
|
|
98
101
|
- Worktree 统一存放在 `~/.clawt/worktrees/<projectName>/` 下
|
|
99
|
-
- 全局配置文件 `~/.clawt/config.json`,postinstall 时自动创建/合并,包含 `autoDeleteBranch`(是否自动删除分支)、`claudeCodeCommand`(Claude Code CLI 启动指令,用于 `run` 和 `resume` 的交互式界面)、`autoPullPush`(merge 后是否自动 pull/push
|
|
102
|
+
- 全局配置文件 `~/.clawt/config.json`,postinstall 时自动创建/合并,包含 `autoDeleteBranch`(是否自动删除分支)、`claudeCodeCommand`(Claude Code CLI 启动指令,用于 `run` 和 `resume` 的交互式界面)、`autoPullPush`(merge 后是否自动 pull/push)、`confirmDestructiveOps`(破坏性操作前是否提示确认,影响 `reset` 和 `validate --clean`)四个配置项。配置项以 `CONFIG_DEFINITIONS` 为单一数据源,`DEFAULT_CONFIG` 和 `CONFIG_DESCRIPTIONS` 均从中派生
|
|
100
103
|
- shell 命令执行有同步(`execCommand` → `execSync`)、异步(`spawnProcess` → `spawn`)和同步带 stdin(`execCommandWithInput` → `execFileSync`)三种方式
|
|
101
104
|
- 项目为纯 ESM(`"type": "module"`),模块导入需带 `.js` 后缀
|
|
102
105
|
- 分支名特殊字符会被 `sanitizeBranchName()` 自动清理
|
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ clawt run -b <branchName>
|
|
|
58
58
|
|
|
59
59
|
每个 `--tasks` 对应一个独立的 worktree,Claude Code 会在各自隔离的环境中并行执行任务。任务完成后会实时通知结果,全部完成后输出汇总信息。
|
|
60
60
|
|
|
61
|
-
不传 `--tasks` 时会创建单个 worktree,并在其中直接启动 Claude Code 交互式界面(通过 `spawnSync` + `inherit stdio`),让用户与 Claude Code 直接交互。启动命令由配置项 `claudeCodeCommand` 指定(默认 `claude
|
|
61
|
+
不传 `--tasks` 时会创建单个 worktree,并在其中直接启动 Claude Code 交互式界面(通过 `spawnSync` + `inherit stdio`),让用户与 Claude Code 直接交互。启动命令由配置项 `claudeCodeCommand` 指定(默认 `claude`)。如果指定的分支已存在,会提示使用 `clawt resume -b <branchName>` 恢复会话。
|
|
62
62
|
|
|
63
63
|
任务执行过程中按 Ctrl+C 可中断所有任务。中断后会根据配置自动清理或询问是否清理本次创建的 worktree 和分支(`autoDeleteBranch: true` 时自动清理)。
|
|
64
64
|
|
|
@@ -76,18 +76,32 @@ clawt run -b feature-login
|
|
|
76
76
|
### `clawt resume` — 在已有 worktree 中恢复 Claude Code 会话
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
+
# 指定分支名(支持模糊匹配)
|
|
79
80
|
clawt resume -b <branchName>
|
|
81
|
+
|
|
82
|
+
# 不指定分支名(列出所有分支供选择)
|
|
83
|
+
clawt resume
|
|
80
84
|
```
|
|
81
85
|
|
|
82
86
|
| 参数 | 必填 | 说明 |
|
|
83
87
|
| ---- | ---- | ---- |
|
|
84
|
-
| `-b` |
|
|
88
|
+
| `-b` | 否 | 要恢复的分支名(支持模糊匹配,不传则列出所有分支供选择) |
|
|
85
89
|
|
|
86
90
|
在之前通过 `clawt run` 或 `clawt create` 创建的 worktree 中重新打开 Claude Code 交互式界面,继续之前的工作。启动命令由配置项 `claudeCodeCommand` 指定(默认 `claude`)。
|
|
87
91
|
|
|
92
|
+
**分支匹配策略:**
|
|
93
|
+
- 传 `-b` 时,优先精确匹配分支名;未精确匹配则进行模糊匹配(子串匹配,大小写不敏感);模糊匹配到多个时通过交互列表选择;无匹配时报错并列出所有可用分支
|
|
94
|
+
- 不传 `-b` 时,列出当前项目所有可用分支供交互式选择(仅 1 个时自动使用)
|
|
95
|
+
|
|
88
96
|
```bash
|
|
89
|
-
#
|
|
97
|
+
# 精确匹配
|
|
90
98
|
clawt resume -b feature-login
|
|
99
|
+
|
|
100
|
+
# 模糊匹配(匹配包含 "login" 的分支)
|
|
101
|
+
clawt resume -b login
|
|
102
|
+
|
|
103
|
+
# 交互式选择所有分支
|
|
104
|
+
clawt resume
|
|
91
105
|
```
|
|
92
106
|
|
|
93
107
|
### `clawt validate` — 在主 worktree 验证分支变更
|
|
@@ -146,7 +160,7 @@ clawt merge -b <branchName> [-m <commitMessage>]
|
|
|
146
160
|
| `-b` | 是 | 要合并的分支名 |
|
|
147
161
|
| `-m` | 否 | 提交信息(目标 worktree 工作区有修改时必填) |
|
|
148
162
|
|
|
149
|
-
将目标 worktree 的变更合并到主 worktree
|
|
163
|
+
将目标 worktree 的变更合并到主 worktree 的当前分支。如果配置了 `autoPullPush: true`,合并后会自动推送到远程仓库。如果目标 worktree 工作区有未提交的修改,需要通过 `-m` 提供提交信息;如果目标 worktree 已经提交过(工作区干净但有本地提交),可以省略 `-m` 直接合并。merge 成功后会询问是否清理对应的 worktree 和分支(如果配置了 `autoDeleteBranch: true` 则自动清理)。
|
|
150
164
|
|
|
151
165
|
如果检测到目标分支存在 `clawt sync` 产生的临时提交(auto-save commit),会自动提示是否将所有提交压缩(squash)为一个。用户选择压缩后,所有 commit 会被 reset 到暂存区:如果提供了 `-m` 则直接提交并继续合并流程;如果未提供 `-m` 则提示用户前往目标 worktree 自行提交后重新执行 merge。
|
|
152
166
|
|
|
@@ -177,15 +191,27 @@ clawt remove -b feature-scheme
|
|
|
177
191
|
clawt remove -b feature-scheme-2
|
|
178
192
|
```
|
|
179
193
|
|
|
180
|
-
|
|
194
|
+
移除时会询问是否同时删除对应的本地分支。移除 worktree 时会自动清理对应的 validate 快照;`--all` 模式还会清理整个项目的快照目录。批量移除时单个失败不会中断整个流程,最后汇总报告失败项。
|
|
181
195
|
|
|
182
196
|
### `clawt list` — 列出当前项目所有 worktree
|
|
183
197
|
|
|
184
198
|
```bash
|
|
185
|
-
clawt list
|
|
199
|
+
clawt list [--json]
|
|
186
200
|
```
|
|
187
201
|
|
|
188
|
-
|
|
202
|
+
| 参数 | 必填 | 说明 |
|
|
203
|
+
| ---- | ---- | ---- |
|
|
204
|
+
| `--json` | 否 | 以 JSON 格式输出(仅包含 path 和 branch) |
|
|
205
|
+
|
|
206
|
+
列出当前项目在 `~/.clawt/worktrees/` 下的所有 worktree 及对应分支。指定 `--json` 时以 JSON 格式输出,便于脚本解析。
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# 文本格式输出(默认)
|
|
210
|
+
clawt list
|
|
211
|
+
|
|
212
|
+
# JSON 格式输出
|
|
213
|
+
clawt list --json
|
|
214
|
+
```
|
|
189
215
|
|
|
190
216
|
### `clawt reset` — 重置主 worktree 工作区和暂存区
|
|
191
217
|
|
|
@@ -193,7 +219,7 @@ clawt list
|
|
|
193
219
|
clawt reset
|
|
194
220
|
```
|
|
195
221
|
|
|
196
|
-
重置主 worktree 的工作区和暂存区(`git reset --hard` + `git clean -f
|
|
222
|
+
重置主 worktree 的工作区和暂存区(`git reset --hard` + `git clean -f`),恢复到干净状态。如果配置了 `confirmDestructiveOps: true`(默认),执行前会提示确认。与 `clawt validate --clean` 不同,`reset` 不会删除 validate 快照文件,适用于只想清空变更而保留快照以便后续增量 validate 的场景。如果工作区和暂存区已是干净状态,会提示无需重置。
|
|
197
223
|
|
|
198
224
|
```bash
|
|
199
225
|
# 重置主 worktree 工作区和暂存区
|
|
@@ -216,7 +242,8 @@ clawt config
|
|
|
216
242
|
{
|
|
217
243
|
"autoDeleteBranch": false,
|
|
218
244
|
"claudeCodeCommand": "claude",
|
|
219
|
-
"autoPullPush": false
|
|
245
|
+
"autoPullPush": false,
|
|
246
|
+
"confirmDestructiveOps": true
|
|
220
247
|
}
|
|
221
248
|
```
|
|
222
249
|
|
|
@@ -225,6 +252,7 @@ clawt config
|
|
|
225
252
|
| `autoDeleteBranch` | `boolean` | `false` | 移除 worktree 时自动删除对应本地分支;merge 成功后自动清理 worktree 和分支;run 中断后自动清理本次创建的 worktree 和分支 |
|
|
226
253
|
| `claudeCodeCommand` | `string` | `"claude"` | Claude Code CLI 启动指令,用于 `clawt run` 不传 `--tasks` 时和 `clawt resume` 在 worktree 中打开交互式界面 |
|
|
227
254
|
| `autoPullPush` | `boolean` | `false` | merge 成功后是否自动执行 git pull 和 git push |
|
|
255
|
+
| `confirmDestructiveOps` | `boolean` | `true` | 执行破坏性操作(reset、validate --clean)前是否提示确认 |
|
|
228
256
|
|
|
229
257
|
## 分支名规则
|
|
230
258
|
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,8 @@ var MESSAGES = {
|
|
|
28
28
|
CLAUDE_NOT_INSTALLED: "Claude Code CLI \u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u5B89\u88C5\uFF1Anpm install -g @anthropic-ai/claude-code",
|
|
29
29
|
/** 分支已存在 */
|
|
30
30
|
BRANCH_EXISTS: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u65E0\u6CD5\u521B\u5EFA`,
|
|
31
|
+
/** 分支已存在时提示使用 resume */
|
|
32
|
+
BRANCH_EXISTS_USE_RESUME: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u4F7F\u7528 clawt resume -b ${name} \u6062\u590D\u4F1A\u8BDD`,
|
|
31
33
|
/** 分支名清理后为空 */
|
|
32
34
|
BRANCH_NAME_EMPTY: (original) => `\u5206\u652F\u540D "${original}" \u4E2D\u4E0D\u5305\u542B\u5408\u6CD5\u5B57\u7B26\uFF0C\u65E0\u6CD5\u521B\u5EFA\u5206\u652F`,
|
|
33
35
|
/** 分支名被转换 */
|
|
@@ -122,7 +124,17 @@ var MESSAGES = {
|
|
|
122
124
|
RESET_ALREADY_CLEAN: "\u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u662F\u5E72\u51C0\u72B6\u6001\uFF0C\u65E0\u9700\u91CD\u7F6E",
|
|
123
125
|
/** 批量移除部分失败 */
|
|
124
126
|
REMOVE_PARTIAL_FAILURE: (failures) => `\u4EE5\u4E0B worktree \u79FB\u9664\u5931\u8D25\uFF1A
|
|
125
|
-
${failures.map((f) => ` \u2717 ${f.path}: ${f.error}`).join("\n")}
|
|
127
|
+
${failures.map((f) => ` \u2717 ${f.path}: ${f.error}`).join("\n")}`,
|
|
128
|
+
/** resume 无可用 worktree */
|
|
129
|
+
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",
|
|
130
|
+
/** resume 模糊匹配无结果,列出可用分支 */
|
|
131
|
+
RESUME_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
132
|
+
\u53EF\u7528\u5206\u652F\uFF1A
|
|
133
|
+
${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
134
|
+
/** resume 交互选择提示 */
|
|
135
|
+
RESUME_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u6062\u590D\u7684\u5206\u652F",
|
|
136
|
+
/** resume 模糊匹配到多个结果提示 */
|
|
137
|
+
RESUME_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
|
|
126
138
|
};
|
|
127
139
|
|
|
128
140
|
// src/constants/exitCodes.ts
|
|
@@ -696,15 +708,33 @@ function removeProjectSnapshots(projectName) {
|
|
|
696
708
|
// src/commands/list.ts
|
|
697
709
|
import chalk2 from "chalk";
|
|
698
710
|
function registerListCommand(program2) {
|
|
699
|
-
program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").action(() => {
|
|
700
|
-
handleList();
|
|
711
|
+
program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
|
|
712
|
+
handleList(options);
|
|
701
713
|
});
|
|
702
714
|
}
|
|
703
|
-
function handleList() {
|
|
715
|
+
function handleList(options) {
|
|
704
716
|
validateMainWorktree();
|
|
705
717
|
const projectName = getProjectName();
|
|
706
718
|
const worktrees = getProjectWorktrees();
|
|
707
719
|
logger.info(`list \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}\uFF0C\u5171 ${worktrees.length} \u4E2A worktree`);
|
|
720
|
+
if (options.json) {
|
|
721
|
+
printListAsJson(projectName, worktrees);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
printListAsText(projectName, worktrees);
|
|
725
|
+
}
|
|
726
|
+
function printListAsJson(projectName, worktrees) {
|
|
727
|
+
const result = {
|
|
728
|
+
project: projectName,
|
|
729
|
+
total: worktrees.length,
|
|
730
|
+
worktrees: worktrees.map((wt) => ({
|
|
731
|
+
path: wt.path,
|
|
732
|
+
branch: wt.branch
|
|
733
|
+
}))
|
|
734
|
+
};
|
|
735
|
+
console.log(JSON.stringify(result, null, 2));
|
|
736
|
+
}
|
|
737
|
+
function printListAsText(projectName, worktrees) {
|
|
708
738
|
printInfo(`\u5F53\u524D\u9879\u76EE: ${projectName}
|
|
709
739
|
`);
|
|
710
740
|
if (worktrees.length === 0) {
|
|
@@ -928,6 +958,10 @@ async function handleRun(options) {
|
|
|
928
958
|
validateMainWorktree();
|
|
929
959
|
validateClaudeCodeInstalled();
|
|
930
960
|
if (!options.tasks || options.tasks.length === 0) {
|
|
961
|
+
const sanitized = sanitizeBranchName(options.branch);
|
|
962
|
+
if (checkBranchExists(sanitized)) {
|
|
963
|
+
throw new ClawtError(MESSAGES.BRANCH_EXISTS_USE_RESUME(sanitized));
|
|
964
|
+
}
|
|
931
965
|
const worktrees2 = createWorktrees(options.branch, 1);
|
|
932
966
|
const worktree = worktrees2[0];
|
|
933
967
|
printSuccess(MESSAGES.WORKTREE_CREATED(1));
|
|
@@ -988,31 +1022,66 @@ async function handleRun(options) {
|
|
|
988
1022
|
}
|
|
989
1023
|
|
|
990
1024
|
// src/commands/resume.ts
|
|
1025
|
+
import Enquirer2 from "enquirer";
|
|
991
1026
|
function registerResumeCommand(program2) {
|
|
992
|
-
program2.command("resume").description("\u5728\u5DF2\u6709 worktree \u4E2D\u6062\u590D Claude Code \u4EA4\u4E92\u5F0F\u4F1A\u8BDD").
|
|
993
|
-
handleResume(options);
|
|
1027
|
+
program2.command("resume").description("\u5728\u5DF2\u6709 worktree \u4E2D\u6062\u590D Claude Code \u4EA4\u4E92\u5F0F\u4F1A\u8BDD").option("-b, --branch <branchName>", "\u8981\u6062\u590D\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").action(async (options) => {
|
|
1028
|
+
await handleResume(options);
|
|
994
1029
|
});
|
|
995
1030
|
}
|
|
996
|
-
function
|
|
1031
|
+
function findExactMatch(worktrees, branchName) {
|
|
1032
|
+
return worktrees.find((wt) => wt.branch === branchName);
|
|
1033
|
+
}
|
|
1034
|
+
function findFuzzyMatches(worktrees, keyword) {
|
|
1035
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
1036
|
+
return worktrees.filter((wt) => wt.branch.toLowerCase().includes(lowerKeyword));
|
|
1037
|
+
}
|
|
1038
|
+
async function promptSelectBranch(worktrees, message) {
|
|
1039
|
+
const selectedBranch = await new Enquirer2.Select({
|
|
1040
|
+
message,
|
|
1041
|
+
choices: worktrees.map((wt) => ({
|
|
1042
|
+
name: wt.branch,
|
|
1043
|
+
message: wt.branch
|
|
1044
|
+
}))
|
|
1045
|
+
}).run();
|
|
1046
|
+
return worktrees.find((wt) => wt.branch === selectedBranch);
|
|
1047
|
+
}
|
|
1048
|
+
async function resolveTargetWorktree(branchName) {
|
|
997
1049
|
const worktrees = getProjectWorktrees();
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
throw new ClawtError(MESSAGES.WORKTREE_NOT_FOUND(branchName));
|
|
1050
|
+
if (worktrees.length === 0) {
|
|
1051
|
+
throw new ClawtError(MESSAGES.RESUME_NO_WORKTREES);
|
|
1001
1052
|
}
|
|
1002
|
-
|
|
1053
|
+
if (!branchName) {
|
|
1054
|
+
if (worktrees.length === 1) {
|
|
1055
|
+
return worktrees[0];
|
|
1056
|
+
}
|
|
1057
|
+
return promptSelectBranch(worktrees, MESSAGES.RESUME_SELECT_BRANCH);
|
|
1058
|
+
}
|
|
1059
|
+
const exactMatch = findExactMatch(worktrees, branchName);
|
|
1060
|
+
if (exactMatch) {
|
|
1061
|
+
return exactMatch;
|
|
1062
|
+
}
|
|
1063
|
+
const fuzzyMatches = findFuzzyMatches(worktrees, branchName);
|
|
1064
|
+
if (fuzzyMatches.length === 1) {
|
|
1065
|
+
return fuzzyMatches[0];
|
|
1066
|
+
}
|
|
1067
|
+
if (fuzzyMatches.length > 1) {
|
|
1068
|
+
return promptSelectBranch(fuzzyMatches, MESSAGES.RESUME_MULTIPLE_MATCHES(branchName));
|
|
1069
|
+
}
|
|
1070
|
+
const allBranches = worktrees.map((wt) => wt.branch);
|
|
1071
|
+
throw new ClawtError(MESSAGES.RESUME_NO_MATCH(branchName, allBranches));
|
|
1003
1072
|
}
|
|
1004
|
-
function handleResume(options) {
|
|
1073
|
+
async function handleResume(options) {
|
|
1005
1074
|
validateMainWorktree();
|
|
1006
1075
|
validateClaudeCodeInstalled();
|
|
1007
|
-
logger.info(`resume \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch}`);
|
|
1008
|
-
const worktree =
|
|
1076
|
+
logger.info(`resume \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch ?? "(\u672A\u6307\u5B9A)"}`);
|
|
1077
|
+
const worktree = await resolveTargetWorktree(options.branch);
|
|
1009
1078
|
launchInteractiveClaude(worktree);
|
|
1010
1079
|
}
|
|
1011
1080
|
|
|
1012
1081
|
// src/commands/validate.ts
|
|
1013
1082
|
import { join as join4 } from "path";
|
|
1014
1083
|
import { existsSync as existsSync6 } from "fs";
|
|
1015
|
-
import
|
|
1084
|
+
import Enquirer3 from "enquirer";
|
|
1016
1085
|
function registerValidateCommand(program2) {
|
|
1017
1086
|
program2.command("validate").description("\u5728\u4E3B worktree \u9A8C\u8BC1\u67D0\u4E2A worktree \u5206\u652F\u7684\u53D8\u66F4").requiredOption("-b, --branch <branchName>", "\u8981\u9A8C\u8BC1\u7684\u5206\u652F\u540D").option("--clean", "\u6E05\u7406 validate \u72B6\u6001\uFF08\u91CD\u7F6E\u4E3B worktree \u5E76\u5220\u9664\u5FEB\u7167\uFF09").action(async (options) => {
|
|
1018
1087
|
await handleValidate(options);
|
|
@@ -1020,7 +1089,7 @@ function registerValidateCommand(program2) {
|
|
|
1020
1089
|
}
|
|
1021
1090
|
async function handleDirtyMainWorktree(mainWorktreePath) {
|
|
1022
1091
|
printWarning("\u4E3B worktree \u5F53\u524D\u5206\u652F\u6709\u672A\u63D0\u4EA4\u7684\u66F4\u6539\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F\uFF1A\n");
|
|
1023
|
-
const choice = await new
|
|
1092
|
+
const choice = await new Enquirer3.Select({
|
|
1024
1093
|
message: "\u9009\u62E9\u5904\u7406\u65B9\u5F0F",
|
|
1025
1094
|
choices: [
|
|
1026
1095
|
{
|
|
@@ -1205,7 +1274,7 @@ async function shouldCleanupAfterMerge(branchName) {
|
|
|
1205
1274
|
printInfo(`\u5DF2\u914D\u7F6E\u81EA\u52A8\u5220\u9664\uFF0Cmerge \u6210\u529F\u540E\u5C06\u81EA\u52A8\u6E05\u7406 worktree \u548C\u5206\u652F: ${branchName}`);
|
|
1206
1275
|
return true;
|
|
1207
1276
|
}
|
|
1208
|
-
return confirmAction(
|
|
1277
|
+
return confirmAction(`\u662F\u5426\u5220\u9664\u5BF9\u5E94\u7684 worktree \u548C\u5206\u652F (${branchName})\uFF1F`);
|
|
1209
1278
|
}
|
|
1210
1279
|
function cleanupWorktreeAndBranch(worktreePath, branchName) {
|
|
1211
1280
|
cleanupWorktrees([{ path: worktreePath, branch: branchName }]);
|
|
@@ -1231,7 +1300,6 @@ async function handleMerge(options) {
|
|
|
1231
1300
|
if (shouldExit) {
|
|
1232
1301
|
return;
|
|
1233
1302
|
}
|
|
1234
|
-
const shouldCleanup = await shouldCleanupAfterMerge(options.branch);
|
|
1235
1303
|
const targetClean = isWorkingDirClean(targetWorktreePath);
|
|
1236
1304
|
if (!targetClean) {
|
|
1237
1305
|
if (!options.message) {
|
|
@@ -1267,6 +1335,7 @@ async function handleMerge(options) {
|
|
|
1267
1335
|
} else {
|
|
1268
1336
|
printSuccess(MESSAGES.MERGE_SUCCESS_NO_MESSAGE(options.branch, autoPullPush));
|
|
1269
1337
|
}
|
|
1338
|
+
const shouldCleanup = await shouldCleanupAfterMerge(options.branch);
|
|
1270
1339
|
if (shouldCleanup) {
|
|
1271
1340
|
cleanupWorktreeAndBranch(targetWorktreePath, options.branch);
|
|
1272
1341
|
}
|
package/docs/spec.md
CHANGED
|
@@ -106,6 +106,7 @@ const projectName = path.basename(projectRoot);
|
|
|
106
106
|
1. 将所有非法字符替换为 `-`
|
|
107
107
|
2. 将连续的 `-` 压缩为一个 `-`
|
|
108
108
|
3. 去除首尾 `-`
|
|
109
|
+
4. 如果转换后结果为空串(原始分支名不包含任何合法字符),报错退出
|
|
109
110
|
|
|
110
111
|
**示例输出:**
|
|
111
112
|
|
|
@@ -164,7 +165,7 @@ git show-ref --verify refs/heads/<branchName> 2>/dev/null
|
|
|
164
165
|
| `clawt validate` | 在主 worktree 验证某个 worktree 分支的变更 | 5.4 |
|
|
165
166
|
| `clawt merge` | 合并某个已验证的 worktree 分支到主 worktree | 5.6 |
|
|
166
167
|
| `clawt remove` | 移除 worktree(支持单个/批量/全部) | 5.5 |
|
|
167
|
-
| `clawt list` | 列出当前项目所有 worktree
|
|
168
|
+
| `clawt list` | 列出当前项目所有 worktree(支持 `--json` 格式输出) | 5.8 |
|
|
168
169
|
| `clawt config` | 查看全局配置 | 5.10 |
|
|
169
170
|
| `clawt resume` | 在已有 worktree 中恢复 Claude Code 交互式会话 | 5.11 |
|
|
170
171
|
| `clawt sync` | 将主分支最新代码同步到目标 worktree | 5.12 |
|
|
@@ -262,7 +263,7 @@ clawt run -b <branchName>
|
|
|
262
263
|
|
|
263
264
|
**运行流程:**
|
|
264
265
|
|
|
265
|
-
1. 若传了 `--tasks`,解析得到任务数组 `tasks[]
|
|
266
|
+
1. 若传了 `--tasks`,解析得到任务数组 `tasks[]`;若未传,先检测分支是否已存在(已存在则提示使用 `clawt resume -b <branchName>` 恢复会话),然后创建单个 worktree 并启动 Claude Code 交互式界面(流程结束,不进入后续并行执行阶段)
|
|
266
267
|
2. `n = tasks.length`
|
|
267
268
|
3. 按照 **5.1** 的流程创建 `n` 个 worktree
|
|
268
269
|
4. 对每个 worktree 并行启动 Claude Code CLI:
|
|
@@ -371,9 +372,10 @@ validate 命令引入了**快照(snapshot)机制**来支持增量对比。
|
|
|
371
372
|
当指定 `--clean` 选项时,执行清理逻辑后直接返回,不进入常规 validate 流程:
|
|
372
373
|
|
|
373
374
|
1. **主 worktree 校验** (2.1)
|
|
374
|
-
2.
|
|
375
|
-
3.
|
|
376
|
-
4.
|
|
375
|
+
2. 如果配置项 `confirmDestructiveOps` 为 `true`,提示确认(显示即将执行的危险指令和操作后果),用户取消则退出
|
|
376
|
+
3. 如果主 worktree 有未提交更改,执行 `git reset --hard` + `git clean -fd` 清空
|
|
377
|
+
4. 删除对应分支的快照文件
|
|
378
|
+
5. 输出清理成功提示
|
|
377
379
|
|
|
378
380
|
#### 首次 validate(无历史快照)
|
|
379
381
|
|
|
@@ -550,11 +552,17 @@ git worktree remove -f <worktree路径>
|
|
|
550
552
|
|
|
551
553
|
# 如果用户选择了删除分支
|
|
552
554
|
git branch -D <branchName>
|
|
555
|
+
|
|
556
|
+
# 清理该分支对应的 validate 快照
|
|
553
557
|
```
|
|
554
558
|
|
|
555
559
|
6. 如果配置文件 `~/.clawt/config.json` 中 `autoDeleteBranch` 为 `true`,则跳过询问,直接删除分支。
|
|
556
560
|
|
|
557
|
-
7.
|
|
561
|
+
7. 如果使用 `--all` 模式,额外清理整个项目的 validate 快照目录。
|
|
562
|
+
|
|
563
|
+
8. 移除完成后,清理空目录(如果 `~/.clawt/worktrees/<project>/` 下已无 worktree,则删除该项目目录)。
|
|
564
|
+
|
|
565
|
+
9. 批量移除时,单个 worktree 移除失败不会中断整个流程,而是收集所有失败项,最后汇总报告。
|
|
558
566
|
|
|
559
567
|
---
|
|
560
568
|
|
|
@@ -620,25 +628,33 @@ clawt merge -b <branchName> [-m <commitMessage>]
|
|
|
620
628
|
- 检查 merge 退出码及 `git status` 是否存在冲突
|
|
621
629
|
- **有冲突** → 提示 `合并存在冲突,请手动处理`,退出
|
|
622
630
|
- **无冲突** → 继续
|
|
623
|
-
7.
|
|
631
|
+
7. **推送(受 `autoPullPush` 配置控制)**
|
|
624
632
|
```bash
|
|
633
|
+
# 仅当 autoPullPush 为 true 时执行
|
|
625
634
|
git pull
|
|
626
635
|
git push
|
|
627
636
|
```
|
|
628
637
|
8. **输出成功提示**
|
|
629
638
|
|
|
630
639
|
```
|
|
631
|
-
# 提供了 -m
|
|
640
|
+
# 提供了 -m 且已推送时
|
|
632
641
|
✓ 分支 feature-scheme-1 已成功合并到当前分支
|
|
633
642
|
提交信息: <commitMessage>
|
|
634
643
|
已推送到远程仓库
|
|
635
644
|
|
|
636
|
-
#
|
|
645
|
+
# 提供了 -m 但未推送时
|
|
646
|
+
✓ 分支 feature-scheme-1 已成功合并到当前分支
|
|
647
|
+
提交信息: <commitMessage>
|
|
648
|
+
|
|
649
|
+
# 未提供 -m 且已推送时
|
|
637
650
|
✓ 分支 feature-scheme-1 已成功合并到当前分支
|
|
638
651
|
已推送到远程仓库
|
|
652
|
+
|
|
653
|
+
# 未提供 -m 且未推送时
|
|
654
|
+
✓ 分支 feature-scheme-1 已成功合并到当前分支
|
|
639
655
|
```
|
|
640
656
|
|
|
641
|
-
9. **merge
|
|
657
|
+
9. **merge 成功后确认并清理 worktree 和分支(可选)**
|
|
642
658
|
- 如果配置文件中 `autoDeleteBranch` 为 `true`,自动执行清理
|
|
643
659
|
- 否则交互式询问用户是否清理
|
|
644
660
|
- 用户确认后,依次执行:
|
|
@@ -656,7 +672,7 @@ clawt merge -b <branchName> [-m <commitMessage>]
|
|
|
656
672
|
10. **清理 validate 快照**
|
|
657
673
|
- merge 成功后,如果存在该分支的 validate 快照(`~/.clawt/validate-snapshots/<project>/<branchName>.tree`),自动删除该快照文件(merge 成功后快照已无意义)
|
|
658
674
|
|
|
659
|
-
> **注意:**
|
|
675
|
+
> **注意:** 清理确认和清理操作均在 merge 成功后执行。只有 merge 成功才会询问用户是否清理 worktree 和分支,避免 merge 冲突时用户被提前询问造成困惑。
|
|
660
676
|
|
|
661
677
|
---
|
|
662
678
|
|
|
@@ -680,7 +696,8 @@ clawt merge -b <branchName> [-m <commitMessage>]
|
|
|
680
696
|
{
|
|
681
697
|
"autoDeleteBranch": false,
|
|
682
698
|
"claudeCodeCommand": "claude",
|
|
683
|
-
"autoPullPush": false
|
|
699
|
+
"autoPullPush": false,
|
|
700
|
+
"confirmDestructiveOps": true
|
|
684
701
|
}
|
|
685
702
|
```
|
|
686
703
|
|
|
@@ -691,6 +708,7 @@ clawt merge -b <branchName> [-m <commitMessage>]
|
|
|
691
708
|
| `autoDeleteBranch` | `boolean` | `false` | 移除 worktree 时是否自动删除对应本地分支(无需每次确认);merge 成功后是否自动清理 worktree 和分支;run 任务被中断(Ctrl+C)后是否自动清理本次创建的 worktree 和分支 |
|
|
692
709
|
| `claudeCodeCommand` | `string` | `"claude"` | Claude Code CLI 启动指令,用于 `clawt run` 不传 `--tasks` 时和 `clawt resume` 在 worktree 中打开交互式界面 |
|
|
693
710
|
| `autoPullPush` | `boolean` | `false` | merge 成功后是否自动执行 git pull 和 git push |
|
|
711
|
+
| `confirmDestructiveOps` | `boolean` | `true` | 执行破坏性操作(reset、validate --clean)前是否提示确认 |
|
|
694
712
|
|
|
695
713
|
---
|
|
696
714
|
|
|
@@ -699,18 +717,26 @@ clawt merge -b <branchName> [-m <commitMessage>]
|
|
|
699
717
|
**命令:**
|
|
700
718
|
|
|
701
719
|
```bash
|
|
702
|
-
clawt list
|
|
720
|
+
clawt list [--json]
|
|
703
721
|
```
|
|
704
722
|
|
|
723
|
+
**参数:**
|
|
724
|
+
|
|
725
|
+
| 参数 | 必填 | 说明 |
|
|
726
|
+
| -------- | ---- | ---------------------------------------- |
|
|
727
|
+
| `--json` | 否 | 以 JSON 格式输出(仅包含 path 和 branch) |
|
|
728
|
+
|
|
705
729
|
**运行流程:**
|
|
706
730
|
|
|
707
731
|
1. **主 worktree 校验** (2.1)
|
|
708
732
|
2. **获取项目名** (2.2)
|
|
709
733
|
3. 扫描 `~/.clawt/worktrees/<project>/` 目录
|
|
710
734
|
4. 对每个子目录,验证是否为有效的 git worktree(`git worktree list` 交叉验证)
|
|
711
|
-
5.
|
|
735
|
+
5. 根据 `--json` 选项决定输出格式:
|
|
736
|
+
- 指定 `--json` → 以 JSON 格式输出
|
|
737
|
+
- 未指定 → 以文本格式输出
|
|
712
738
|
|
|
713
|
-
|
|
739
|
+
**文本输出格式(默认):**
|
|
714
740
|
|
|
715
741
|
```
|
|
716
742
|
当前项目: main-project
|
|
@@ -731,6 +757,25 @@ clawt list
|
|
|
731
757
|
(无 worktree)
|
|
732
758
|
```
|
|
733
759
|
|
|
760
|
+
**JSON 输出格式(`--json`):**
|
|
761
|
+
|
|
762
|
+
```json
|
|
763
|
+
{
|
|
764
|
+
"project": "main-project",
|
|
765
|
+
"total": 4,
|
|
766
|
+
"worktrees": [
|
|
767
|
+
{
|
|
768
|
+
"path": "~/.clawt/worktrees/main-project/feature-scheme-1",
|
|
769
|
+
"branch": "feature-scheme-1"
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
"path": "~/.clawt/worktrees/main-project/feature-scheme-2",
|
|
773
|
+
"branch": "feature-scheme-2"
|
|
774
|
+
}
|
|
775
|
+
]
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
734
779
|
---
|
|
735
780
|
|
|
736
781
|
### 5.9 日志系统
|
|
@@ -797,6 +842,9 @@ clawt config
|
|
|
797
842
|
autoPullPush: false
|
|
798
843
|
merge 成功后是否自动执行 git pull 和 git push
|
|
799
844
|
|
|
845
|
+
confirmDestructiveOps: true
|
|
846
|
+
执行破坏性操作(reset、validate --clean)前是否提示确认
|
|
847
|
+
|
|
800
848
|
────────────────────────────────────────
|
|
801
849
|
|
|
802
850
|
```
|
|
@@ -808,14 +856,18 @@ clawt config
|
|
|
808
856
|
**命令:**
|
|
809
857
|
|
|
810
858
|
```bash
|
|
859
|
+
# 指定分支名(支持模糊匹配)
|
|
811
860
|
clawt resume -b <branchName>
|
|
861
|
+
|
|
862
|
+
# 不指定分支名(列出所有分支供选择)
|
|
863
|
+
clawt resume
|
|
812
864
|
```
|
|
813
865
|
|
|
814
866
|
**参数:**
|
|
815
867
|
|
|
816
868
|
| 参数 | 必填 | 说明 |
|
|
817
869
|
| ---- | ---- | ----------------------------------------------------- |
|
|
818
|
-
| `-b` |
|
|
870
|
+
| `-b` | 否 | 要恢复的分支名(支持模糊匹配,不传则列出所有分支供选择) |
|
|
819
871
|
|
|
820
872
|
**使用场景:**
|
|
821
873
|
|
|
@@ -825,9 +877,18 @@ clawt resume -b <branchName>
|
|
|
825
877
|
|
|
826
878
|
1. **主 worktree 校验** (2.1)
|
|
827
879
|
2. **Claude Code CLI 校验**:确认 `claude` CLI 可用
|
|
828
|
-
3.
|
|
829
|
-
-
|
|
830
|
-
|
|
880
|
+
3. **解析目标 worktree**:根据 `-b` 参数解析目标 worktree,匹配策略如下:
|
|
881
|
+
- **未传 `-b` 参数**:
|
|
882
|
+
- 获取当前项目所有 worktree
|
|
883
|
+
- 无可用 worktree → 报错退出
|
|
884
|
+
- 仅 1 个 worktree → 直接使用,无需选择
|
|
885
|
+
- 多个 worktree → 通过交互式列表(Enquirer.Select)让用户选择
|
|
886
|
+
- **传了 `-b` 参数**:
|
|
887
|
+
1. **精确匹配优先**:在 worktree 列表中查找分支名完全相同的 worktree,找到则直接使用
|
|
888
|
+
2. **模糊匹配**(子串匹配,大小写不敏感):
|
|
889
|
+
- 唯一匹配 → 直接使用
|
|
890
|
+
- 多个匹配 → 通过交互式列表让用户从匹配结果中选择
|
|
891
|
+
3. **无匹配** → 报错退出,并列出所有可用分支名
|
|
831
892
|
4. **启动 Claude Code 交互式界面**:通过 `launchInteractiveClaude()` 在目标 worktree 中启动 Claude Code CLI 交互式界面(使用 `spawnSync` + `inherit stdio`)
|
|
832
893
|
|
|
833
894
|
启动命令通过配置项 `claudeCodeCommand`(默认值 `claude`)指定,与 `clawt run` 不传 `--tasks` 时的交互式界面行为一致。
|
|
@@ -872,6 +933,7 @@ clawt sync -b <branchName>
|
|
|
872
933
|
合并存在冲突,请进入目标 worktree 手动解决:
|
|
873
934
|
cd ~/.clawt/worktrees/<project>/<branchName>
|
|
874
935
|
解决冲突后执行 git add . && git merge --continue
|
|
936
|
+
clawt validate -b <branch> 验证变更
|
|
875
937
|
```
|
|
876
938
|
- **无冲突** → 继续
|
|
877
939
|
7. **清除 validate 快照**:合并成功后,如果该分支存在 validate 快照(`.tree` 文件),自动删除(代码基础已变化,旧快照无效)
|
|
@@ -902,12 +964,13 @@ clawt reset
|
|
|
902
964
|
2. **检测工作区状态**:通过 `git status --porcelain` 检测主 worktree 是否有未提交的更改
|
|
903
965
|
- **工作区干净** → 输出提示 `主 worktree 工作区和暂存区已是干净状态,无需重置`,退出
|
|
904
966
|
- **工作区不干净** → 继续
|
|
905
|
-
3.
|
|
967
|
+
3. **确认破坏性操作**:如果配置项 `confirmDestructiveOps` 为 `true`,提示确认(显示即将执行的危险指令和操作后果),用户取消则退出
|
|
968
|
+
4. **重置工作区和暂存区**:
|
|
906
969
|
```bash
|
|
907
970
|
git reset --hard
|
|
908
971
|
git clean -f
|
|
909
972
|
```
|
|
910
|
-
|
|
973
|
+
5. **输出成功提示**:
|
|
911
974
|
```
|
|
912
975
|
✓ 主 worktree 工作区和暂存区已重置
|
|
913
976
|
```
|
package/package.json
CHANGED
package/src/commands/list.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Command } from 'commander';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { MESSAGES } from '../constants/index.js';
|
|
4
4
|
import { logger } from '../logger/index.js';
|
|
5
|
+
import type { ListOptions } from '../types/index.js';
|
|
5
6
|
import {
|
|
6
7
|
validateMainWorktree,
|
|
7
8
|
getProjectName,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
formatWorktreeStatus,
|
|
11
12
|
printInfo,
|
|
12
13
|
} from '../utils/index.js';
|
|
14
|
+
// getWorktreeStatus 和 formatWorktreeStatus 仅在文本模式下使用
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* 注册 list 命令:列出当前项目所有 worktree
|
|
@@ -19,15 +21,17 @@ export function registerListCommand(program: Command): void {
|
|
|
19
21
|
program
|
|
20
22
|
.command('list')
|
|
21
23
|
.description('列出当前项目所有 worktree')
|
|
22
|
-
.
|
|
23
|
-
|
|
24
|
+
.option('--json', '以 JSON 格式输出')
|
|
25
|
+
.action((options: ListOptions) => {
|
|
26
|
+
handleList(options);
|
|
24
27
|
});
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
/**
|
|
28
31
|
* 执行 list 命令的核心逻辑
|
|
32
|
+
* @param {ListOptions} options - 命令选项
|
|
29
33
|
*/
|
|
30
|
-
function handleList(): void {
|
|
34
|
+
function handleList(options: ListOptions): void {
|
|
31
35
|
validateMainWorktree();
|
|
32
36
|
|
|
33
37
|
const projectName = getProjectName();
|
|
@@ -35,6 +39,38 @@ function handleList(): void {
|
|
|
35
39
|
|
|
36
40
|
logger.info(`list 命令执行,项目: ${projectName},共 ${worktrees.length} 个 worktree`);
|
|
37
41
|
|
|
42
|
+
if (options.json) {
|
|
43
|
+
printListAsJson(projectName, worktrees);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
printListAsText(projectName, worktrees);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 以 JSON 格式输出 worktree 列表(仅包含 path 和 branch)
|
|
52
|
+
* @param {string} projectName - 项目名称
|
|
53
|
+
* @param {import('../types/index.js').WorktreeInfo[]} worktrees - worktree 列表
|
|
54
|
+
*/
|
|
55
|
+
function printListAsJson(projectName: string, worktrees: import('../types/index.js').WorktreeInfo[]): void {
|
|
56
|
+
const result = {
|
|
57
|
+
project: projectName,
|
|
58
|
+
total: worktrees.length,
|
|
59
|
+
worktrees: worktrees.map((wt) => ({
|
|
60
|
+
path: wt.path,
|
|
61
|
+
branch: wt.branch,
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
console.log(JSON.stringify(result, null, 2));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 以文本格式输出 worktree 列表
|
|
70
|
+
* @param {string} projectName - 项目名称
|
|
71
|
+
* @param {import('../types/index.js').WorktreeInfo[]} worktrees - worktree 列表
|
|
72
|
+
*/
|
|
73
|
+
function printListAsText(projectName: string, worktrees: import('../types/index.js').WorktreeInfo[]): void {
|
|
38
74
|
printInfo(`当前项目: ${projectName}\n`);
|
|
39
75
|
|
|
40
76
|
if (worktrees.length === 0) {
|
package/src/commands/merge.ts
CHANGED
|
@@ -105,7 +105,7 @@ async function shouldCleanupAfterMerge(branchName: string): Promise<boolean> {
|
|
|
105
105
|
printInfo(`已配置自动删除,merge 成功后将自动清理 worktree 和分支: ${branchName}`);
|
|
106
106
|
return true;
|
|
107
107
|
}
|
|
108
|
-
return confirmAction(
|
|
108
|
+
return confirmAction(`是否删除对应的 worktree 和分支 (${branchName})?`);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
@@ -153,9 +153,6 @@ async function handleMerge(options: MergeOptions): Promise<void> {
|
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
// merge 前确认是否清理 worktree 和分支
|
|
157
|
-
const shouldCleanup = await shouldCleanupAfterMerge(options.branch);
|
|
158
|
-
|
|
159
156
|
// 步骤 4:根据目标 worktree 状态决定是否需要提交
|
|
160
157
|
const targetClean = isWorkingDirClean(targetWorktreePath);
|
|
161
158
|
|
|
@@ -206,7 +203,8 @@ async function handleMerge(options: MergeOptions): Promise<void> {
|
|
|
206
203
|
printSuccess(MESSAGES.MERGE_SUCCESS_NO_MESSAGE(options.branch, autoPullPush));
|
|
207
204
|
}
|
|
208
205
|
|
|
209
|
-
// 步骤 9:merge
|
|
206
|
+
// 步骤 9:merge 成功后确认并清理 worktree 和分支
|
|
207
|
+
const shouldCleanup = await shouldCleanupAfterMerge(options.branch);
|
|
210
208
|
if (shouldCleanup) {
|
|
211
209
|
cleanupWorktreeAndBranch(targetWorktreePath, options.branch);
|
|
212
210
|
}
|
package/src/commands/resume.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
+
import Enquirer from 'enquirer';
|
|
2
3
|
import { logger } from '../logger/index.js';
|
|
3
4
|
import { ClawtError } from '../errors/index.js';
|
|
4
5
|
import { MESSAGES } from '../constants/index.js';
|
|
@@ -18,42 +19,114 @@ export function registerResumeCommand(program: Command): void {
|
|
|
18
19
|
program
|
|
19
20
|
.command('resume')
|
|
20
21
|
.description('在已有 worktree 中恢复 Claude Code 交互式会话')
|
|
21
|
-
.
|
|
22
|
-
.action((options: ResumeOptions) => {
|
|
23
|
-
handleResume(options);
|
|
22
|
+
.option('-b, --branch <branchName>', '要恢复的分支名(支持模糊匹配,不传则列出所有分支)')
|
|
23
|
+
.action(async (options: ResumeOptions) => {
|
|
24
|
+
await handleResume(options);
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
|
-
*
|
|
29
|
+
* 在 worktree 列表中精确匹配分支名
|
|
30
|
+
* @param {WorktreeInfo[]} worktrees - worktree 列表
|
|
29
31
|
* @param {string} branchName - 目标分支名
|
|
30
|
-
* @returns {WorktreeInfo} 匹配的 worktree
|
|
31
|
-
* @throws {ClawtError} 当找不到匹配的 worktree 时抛出
|
|
32
|
+
* @returns {WorktreeInfo | undefined} 匹配的 worktree,未找到返回 undefined
|
|
32
33
|
*/
|
|
33
|
-
function
|
|
34
|
+
function findExactMatch(worktrees: WorktreeInfo[], branchName: string): WorktreeInfo | undefined {
|
|
35
|
+
return worktrees.find((wt) => wt.branch === branchName);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 在 worktree 列表中进行模糊匹配(子串匹配,大小写不敏感)
|
|
40
|
+
* @param {WorktreeInfo[]} worktrees - worktree 列表
|
|
41
|
+
* @param {string} keyword - 匹配关键词
|
|
42
|
+
* @returns {WorktreeInfo[]} 匹配到的 worktree 列表
|
|
43
|
+
*/
|
|
44
|
+
function findFuzzyMatches(worktrees: WorktreeInfo[], keyword: string): WorktreeInfo[] {
|
|
45
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
46
|
+
return worktrees.filter((wt) => wt.branch.toLowerCase().includes(lowerKeyword));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 通过交互式列表让用户从 worktree 列表中选择一个分支
|
|
51
|
+
* @param {WorktreeInfo[]} worktrees - 可供选择的 worktree 列表
|
|
52
|
+
* @param {string} message - 选择提示信息
|
|
53
|
+
* @returns {Promise<WorktreeInfo>} 用户选择的 worktree
|
|
54
|
+
*/
|
|
55
|
+
async function promptSelectBranch(worktrees: WorktreeInfo[], message: string): Promise<WorktreeInfo> {
|
|
56
|
+
// @ts-expect-error enquirer 类型声明未导出 Select 类,但运行时存在
|
|
57
|
+
const selectedBranch: string = await new Enquirer.Select({
|
|
58
|
+
message,
|
|
59
|
+
choices: worktrees.map((wt) => ({
|
|
60
|
+
name: wt.branch,
|
|
61
|
+
message: wt.branch,
|
|
62
|
+
})),
|
|
63
|
+
}).run();
|
|
64
|
+
|
|
65
|
+
return worktrees.find((wt) => wt.branch === selectedBranch)!;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 根据用户输入解析目标 worktree
|
|
70
|
+
* 匹配策略:精确匹配 → 模糊匹配(唯一直接使用,多个交互选择) → 无匹配报错
|
|
71
|
+
* 不传分支名时列出所有可用分支供选择
|
|
72
|
+
* @param {string} [branchName] - 用户输入的分支名(可选)
|
|
73
|
+
* @returns {Promise<WorktreeInfo>} 解析后的目标 worktree
|
|
74
|
+
* @throws {ClawtError} 无可用 worktree 或无匹配结果时抛出
|
|
75
|
+
*/
|
|
76
|
+
async function resolveTargetWorktree(branchName?: string): Promise<WorktreeInfo> {
|
|
34
77
|
const worktrees = getProjectWorktrees();
|
|
35
|
-
const matched = worktrees.find((wt) => wt.branch === branchName);
|
|
36
78
|
|
|
37
|
-
|
|
38
|
-
|
|
79
|
+
// 无可用 worktree,直接报错
|
|
80
|
+
if (worktrees.length === 0) {
|
|
81
|
+
throw new ClawtError(MESSAGES.RESUME_NO_WORKTREES);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 未传 -b 参数:列出所有分支供选择
|
|
85
|
+
if (!branchName) {
|
|
86
|
+
// 只有一个 worktree 时直接使用,无需选择
|
|
87
|
+
if (worktrees.length === 1) {
|
|
88
|
+
return worktrees[0];
|
|
89
|
+
}
|
|
90
|
+
return promptSelectBranch(worktrees, MESSAGES.RESUME_SELECT_BRANCH);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 1. 精确匹配优先
|
|
94
|
+
const exactMatch = findExactMatch(worktrees, branchName);
|
|
95
|
+
if (exactMatch) {
|
|
96
|
+
return exactMatch;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 2. 模糊匹配
|
|
100
|
+
const fuzzyMatches = findFuzzyMatches(worktrees, branchName);
|
|
101
|
+
|
|
102
|
+
// 2a. 唯一匹配,直接使用
|
|
103
|
+
if (fuzzyMatches.length === 1) {
|
|
104
|
+
return fuzzyMatches[0];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 2b. 多个匹配,交互选择
|
|
108
|
+
if (fuzzyMatches.length > 1) {
|
|
109
|
+
return promptSelectBranch(fuzzyMatches, MESSAGES.RESUME_MULTIPLE_MATCHES(branchName));
|
|
39
110
|
}
|
|
40
111
|
|
|
41
|
-
|
|
112
|
+
// 3. 无匹配,抛出错误并列出所有可用分支
|
|
113
|
+
const allBranches = worktrees.map((wt) => wt.branch);
|
|
114
|
+
throw new ClawtError(MESSAGES.RESUME_NO_MATCH(branchName, allBranches));
|
|
42
115
|
}
|
|
43
116
|
|
|
44
117
|
/**
|
|
45
118
|
* 执行 resume 命令的核心逻辑
|
|
46
|
-
*
|
|
119
|
+
* 解析目标 worktree 并恢复 Claude Code 会话
|
|
47
120
|
* @param {ResumeOptions} options - 命令选项
|
|
48
121
|
*/
|
|
49
|
-
function handleResume(options: ResumeOptions): void {
|
|
122
|
+
async function handleResume(options: ResumeOptions): Promise<void> {
|
|
50
123
|
validateMainWorktree();
|
|
51
124
|
validateClaudeCodeInstalled();
|
|
52
125
|
|
|
53
|
-
logger.info(`resume 命令执行,分支: ${options.branch}`);
|
|
126
|
+
logger.info(`resume 命令执行,分支: ${options.branch ?? '(未指定)'}`);
|
|
54
127
|
|
|
55
|
-
//
|
|
56
|
-
const worktree =
|
|
128
|
+
// 解析目标 worktree(精确匹配 / 模糊匹配 / 交互选择)
|
|
129
|
+
const worktree = await resolveTargetWorktree(options.branch);
|
|
57
130
|
|
|
58
131
|
// 启动 Claude Code 交互式界面
|
|
59
132
|
launchInteractiveClaude(worktree);
|
package/src/commands/run.ts
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
validateMainWorktree,
|
|
9
9
|
validateClaudeCodeInstalled,
|
|
10
10
|
createWorktrees,
|
|
11
|
+
sanitizeBranchName,
|
|
12
|
+
checkBranchExists,
|
|
11
13
|
spawnProcess,
|
|
12
14
|
killAllChildProcesses,
|
|
13
15
|
cleanupWorktrees,
|
|
@@ -191,6 +193,12 @@ async function handleRun(options: RunOptions): Promise<void> {
|
|
|
191
193
|
|
|
192
194
|
// 未传 --tasks 时,创建单个 worktree 并打开 Claude Code 交互式界面
|
|
193
195
|
if (!options.tasks || options.tasks.length === 0) {
|
|
196
|
+
// 分支已存在时,提示用户使用 resume 恢复会话
|
|
197
|
+
const sanitized = sanitizeBranchName(options.branch);
|
|
198
|
+
if (checkBranchExists(sanitized)) {
|
|
199
|
+
throw new ClawtError(MESSAGES.BRANCH_EXISTS_USE_RESUME(sanitized));
|
|
200
|
+
}
|
|
201
|
+
|
|
194
202
|
const worktrees = createWorktrees(options.branch, 1);
|
|
195
203
|
const worktree = worktrees[0];
|
|
196
204
|
printSuccess(MESSAGES.WORKTREE_CREATED(1));
|
|
@@ -8,6 +8,9 @@ export const MESSAGES = {
|
|
|
8
8
|
CLAUDE_NOT_INSTALLED: 'Claude Code CLI 未安装,请先安装:npm install -g @anthropic-ai/claude-code',
|
|
9
9
|
/** 分支已存在 */
|
|
10
10
|
BRANCH_EXISTS: (name: string) => `分支 ${name} 已存在,无法创建`,
|
|
11
|
+
/** 分支已存在时提示使用 resume */
|
|
12
|
+
BRANCH_EXISTS_USE_RESUME: (name: string) =>
|
|
13
|
+
`分支 ${name} 已存在,请使用 clawt resume -b ${name} 恢复会话`,
|
|
11
14
|
/** 分支名清理后为空 */
|
|
12
15
|
BRANCH_NAME_EMPTY: (original: string) =>
|
|
13
16
|
`分支名 "${original}" 中不包含合法字符,无法创建分支`,
|
|
@@ -107,4 +110,13 @@ export const MESSAGES = {
|
|
|
107
110
|
/** 批量移除部分失败 */
|
|
108
111
|
REMOVE_PARTIAL_FAILURE: (failures: Array<{ path: string; error: string }>) =>
|
|
109
112
|
`以下 worktree 移除失败:\n${failures.map((f) => ` ✗ ${f.path}: ${f.error}`).join('\n')}`,
|
|
113
|
+
/** resume 无可用 worktree */
|
|
114
|
+
RESUME_NO_WORKTREES: '当前项目没有可用的 worktree,请先通过 clawt run 或 clawt create 创建',
|
|
115
|
+
/** resume 模糊匹配无结果,列出可用分支 */
|
|
116
|
+
RESUME_NO_MATCH: (name: string, branches: string[]) =>
|
|
117
|
+
`未找到与 "${name}" 匹配的分支\n 可用分支:\n${branches.map((b) => ` - ${b}`).join('\n')}`,
|
|
118
|
+
/** resume 交互选择提示 */
|
|
119
|
+
RESUME_SELECT_BRANCH: '请选择要恢复的分支',
|
|
120
|
+
/** resume 模糊匹配到多个结果提示 */
|
|
121
|
+
RESUME_MULTIPLE_MATCHES: (name: string) => `"${name}" 匹配到多个分支,请选择:`,
|
|
110
122
|
} as const;
|
package/src/types/command.ts
CHANGED
|
@@ -40,8 +40,8 @@ export interface RemoveOptions {
|
|
|
40
40
|
|
|
41
41
|
/** resume 命令选项 */
|
|
42
42
|
export interface ResumeOptions {
|
|
43
|
-
/**
|
|
44
|
-
branch
|
|
43
|
+
/** 要恢复的分支名(可选,不传则列出所有分支供选择) */
|
|
44
|
+
branch?: string;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/** sync 命令选项 */
|
|
@@ -49,3 +49,9 @@ export interface SyncOptions {
|
|
|
49
49
|
/** 要同步的分支名 */
|
|
50
50
|
branch: string;
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
/** list 命令选项 */
|
|
54
|
+
export interface ListOptions {
|
|
55
|
+
/** 以 JSON 格式输出 */
|
|
56
|
+
json?: boolean;
|
|
57
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { ClawtConfig, ConfigItemDefinition, ConfigDefinitions } from './config.js';
|
|
2
|
-
export type { CreateOptions, RunOptions, ValidateOptions, MergeOptions, RemoveOptions, ResumeOptions, SyncOptions } from './command.js';
|
|
2
|
+
export type { CreateOptions, RunOptions, ValidateOptions, MergeOptions, RemoveOptions, ResumeOptions, SyncOptions, ListOptions } 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';
|