clawt 2.20.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 (71) hide show
  1. package/README.md +10 -0
  2. package/dist/index.js +395 -133
  3. package/dist/postinstall.js +35 -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 -1905
  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 +1 -1
  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 +2 -2
  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/index.ts +2 -0
  49. package/src/types/command.ts +7 -1
  50. package/src/types/index.ts +2 -1
  51. package/src/types/projectConfig.ts +5 -0
  52. package/src/utils/config.ts +2 -1
  53. package/src/utils/git.ts +18 -0
  54. package/src/utils/index.ts +5 -0
  55. package/src/utils/json.ts +67 -0
  56. package/src/utils/project-config.ts +77 -0
  57. package/src/utils/validate-branch.ts +166 -0
  58. package/src/utils/worktree.ts +6 -2
  59. package/tests/unit/commands/create.test.ts +20 -16
  60. package/tests/unit/commands/init.test.ts +146 -0
  61. package/tests/unit/commands/merge.test.ts +7 -1
  62. package/tests/unit/commands/remove.test.ts +4 -0
  63. package/tests/unit/commands/reset.test.ts +2 -0
  64. package/tests/unit/commands/run.test.ts +2 -0
  65. package/tests/unit/commands/sync.test.ts +6 -0
  66. package/tests/unit/commands/validate.test.ts +13 -0
  67. package/tests/unit/utils/config.test.ts +2 -2
  68. package/tests/unit/utils/project-config.test.ts +136 -0
  69. package/tests/unit/utils/update-checker.test.ts +28 -7
  70. package/tests/unit/utils/validate-branch.test.ts +272 -0
  71. package/tests/unit/utils/worktree.test.ts +6 -0
package/docs/status.md ADDED
@@ -0,0 +1,155 @@
1
+ ### 5.14 项目全局状态总览
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ clawt status [--json]
7
+ ```
8
+
9
+ **参数:**
10
+
11
+ | 参数 | 必填 | 说明 |
12
+ | -------- | ---- | ---------------------------------------- |
13
+ | `--json` | 否 | 以 JSON 格式输出完整状态数据 |
14
+
15
+ **使用场景:**
16
+
17
+ 在管理多个 worktree 时,快速了解项目全局状态:主 worktree 当前分支及干净状态、所有 worktree 的变更情况和与主分支的同步状态、validate 快照摘要。
18
+
19
+ **运行流程:**
20
+
21
+ 1. **主 worktree 校验** (2.1)
22
+ 2. **收集主 worktree 状态**:
23
+ - 获取当前分支名(`getCurrentBranch()`)
24
+ - 检测工作区是否干净(`isWorkingDirClean()`)
25
+ - 获取项目名(`getProjectName()`)
26
+ 3. **收集各 worktree 详细状态**:
27
+ - 获取项目所有 worktree(`getProjectWorktrees()`)
28
+ - 对每个 worktree 收集以下信息:
29
+ - **变更状态**(优先级:合并冲突 > 未提交修改 > 已提交 > 无变更)
30
+ - **行数差异**(新增/删除行数,通过 `getDiffStat()` 获取)
31
+ - **提交差异**(相对于主分支的领先提交数 `getCommitCountAhead()` 和落后提交数 `getCommitCountBehind()`)
32
+ - **快照时间**(validate 快照文件的 mtime,通过 `getSnapshotModifiedTime()` 获取,返回 ISO 8601 时间字符串或 null)
33
+ - **分支创建时间**(通过 `getBranchCreatedAt()` 从 git reflog 获取分支创建时的时间戳)
34
+ 4. **收集 validate 快照摘要**:
35
+ - 通过 `getProjectSnapshotBranches()` 扫描快照目录下的 `.tree` 文件获取所有存在快照的分支名
36
+ - 统计快照总数和孤立快照数(对应 worktree 已不存在的快照)
37
+ 5. **输出状态信息**:
38
+ - 指定 `--json` → 以 JSON 格式输出完整状态数据(`JSON.stringify`)
39
+ - 未指定 → 以文本格式输出
40
+
41
+ **文本输出格式(默认):**
42
+
43
+ 输出分为三个区块:主 Worktree、Worktree 列表、Validate 快照摘要。每个 worktree 条目每行展示一种信息。
44
+
45
+ ```
46
+ ════════════════════════════════════════
47
+ 项目状态总览: main-project
48
+ ════════════════════════════════════════
49
+
50
+ ◆ 主 Worktree
51
+ 分支: main
52
+ 状态: ✓ 干净
53
+
54
+ ────────────────────────────────────────
55
+
56
+ ◆ Worktree 列表 (2 个)
57
+
58
+ ● feature-login [已提交]
59
+ +120 -30
60
+ 3 个本地提交
61
+ 与主分支同步
62
+ 创建于 3 天前
63
+ 上次验证: 2 小时前
64
+
65
+ ● feature-signup [未提交修改]
66
+ +45 -10
67
+ 1 个本地提交
68
+ 落后主分支 2 个提交
69
+ 创建于 1 天前
70
+ ✗ 未验证
71
+
72
+ ────────────────────────────────────────
73
+
74
+ ◆ Validate 快照 (3 个)
75
+ 其中 1 个快照对应的 worktree 已不存在
76
+
77
+ ════════════════════════════════════════
78
+ ```
79
+
80
+ **变更状态标签:**
81
+
82
+ | 状态 | 标签 | 颜色 | 说明 |
83
+ | ----------- | -------------- | ------ | ----------------------------- |
84
+ | `committed` | 已提交 | 绿色 | 有已提交内容,工作区干净 |
85
+ | `uncommitted` | 未提交修改 | 黄色 | 有未提交的修改 |
86
+ | `conflict` | 合并冲突 | 红色 | 存在合并冲突 |
87
+ | `clean` | 无变更 | 灰色 | 工作区干净且无本地提交 |
88
+
89
+ **差异统计展示规则(每项独立一行):**
90
+
91
+ - 行数变更(`+N -N`):仅在有变更时展示,独立一行
92
+ - 本地提交数(`N 个本地提交`):仅在有提交时展示,独立一行(黄色)
93
+ - 与主分支同步状态:始终展示,独立一行(落后时显示黄色,同步时显示绿色)
94
+
95
+ **分支创建时间行:**
96
+
97
+ - 通过 `getBranchCreatedAt()` 从 git reflog 获取分支创建时间,以 `formatRelativeTime()` 格式化为中文相对时间(如"3 天前"、"2 小时前"、"刚刚")
98
+ - 展示为灰色文本 `创建于 X前`,无法获取时不展示
99
+
100
+ **验证状态行:**
101
+
102
+ - 有快照时:显示绿色 `上次验证: X前`(通过 `getSnapshotModifiedTime()` 获取快照文件 mtime,再用 `formatRelativeTime()` 格式化)
103
+ - 无快照时:显示红色 `✗ 未验证` 警示
104
+
105
+ **快照区块:**
106
+
107
+ - 标题显示快照总数
108
+ - 如果存在孤立快照(对应 worktree 已不存在),显示黄色警告 `其中 N 个快照对应的 worktree 已不存在`
109
+ - 无孤立快照时不显示额外信息
110
+
111
+ **JSON 输出格式(`--json`):**
112
+
113
+ ```json
114
+ {
115
+ "main": {
116
+ "branch": "main",
117
+ "isClean": true,
118
+ "projectName": "main-project"
119
+ },
120
+ "worktrees": [
121
+ {
122
+ "path": "~/.clawt/worktrees/main-project/feature-login",
123
+ "branch": "feature-login",
124
+ "changeStatus": "committed",
125
+ "commitsAhead": 3,
126
+ "commitsBehind": 0,
127
+ "snapshotTime": "2025-02-06T12:30:00.000Z",
128
+ "insertions": 120,
129
+ "deletions": 30,
130
+ "createdAt": "2025-02-03T10:00:00.000Z"
131
+ }
132
+ ],
133
+ "snapshots": {
134
+ "total": 3,
135
+ "orphaned": 1
136
+ },
137
+ "totalWorktrees": 1
138
+ }
139
+ ```
140
+
141
+ **实现要点:**
142
+
143
+ - 类型定义在 `src/types/status.ts`:`WorktreeDetailedStatus`(`hasSnapshot` 已改为 `snapshotTime: string | null`,新增 `createdAt: string | null`)、`MainWorktreeStatus`、`SnapshotInfo`、`SnapshotSummary`(新增,包含 `total` 和 `orphaned`)、`StatusResult`(`snapshots` 已从 `SnapshotInfo[]` 改为 `SnapshotSummary`)
144
+ - 消息常量在 `MESSAGES.STATUS_*` 系列,新增:
145
+ - `STATUS_LAST_VALIDATED`:上次验证时间标签(如 `上次验证: 2 小时前`)
146
+ - `STATUS_NOT_VALIDATED`:未验证红色警示文本(`✗ 未验证`)
147
+ - `STATUS_CREATED_AT`:分支创建时间标签(如 `创建于 3 天前`)
148
+ - `STATUS_SNAPSHOT_ORPHANED`:改为接受数量参数的函数(如 `其中 1 个快照对应的 worktree 已不存在`)
149
+ - `getBranchCreatedAt()` 是新增的工具函数(在 `src/utils/git.ts`),通过 `git reflog show <branch> --format=%cI` 获取 reflog 最后一条记录的时间戳(即分支创建时间),返回 ISO 8601 格式字符串或 null
150
+ - `getSnapshotModifiedTime()` 是新增的工具函数(在 `src/utils/validate-snapshot.ts`),通过 `fs.statSync` 获取快照文件的修改时间(mtime),返回 UTC 时区的 ISO 8601 格式字符串(`toISOString()` 格式)或 null
151
+ - `formatRelativeTime()` 是新增的格式化函数(在 `src/utils/formatter.ts`),将 ISO 8601 日期字符串转换为中文相对时间描述(如"3 天前"、"2 小时前"、"刚刚"),无效日期时返回 null
152
+ - `getCommitCountBehind()` 是新增的工具函数(在 `src/utils/git.ts`),通过 `git rev-list --count <branch>..HEAD` 计算落后提交数
153
+ - `getProjectSnapshotBranches()` 是新增的工具函数(在 `src/utils/validate-snapshot.ts`),通过扫描快照目录下的 `.tree` 文件提取分支名列表
154
+
155
+ ---
package/docs/sync.md ADDED
@@ -0,0 +1,114 @@
1
+ ### 5.12 将主分支代码同步到目标 Worktree
2
+
3
+ **命令:**
4
+
5
+ ```bash
6
+ # 指定分支名(支持模糊匹配)
7
+ clawt sync -b <branchName>
8
+
9
+ # 不指定分支名(列出所有分支供选择)
10
+ clawt sync
11
+ ```
12
+
13
+ **参数:**
14
+
15
+ | 参数 | 必填 | 说明 |
16
+ | ---- | ---- | ------------------------------------------------------------------------ |
17
+ | `-b` | 否 | 要同步的分支名(支持模糊匹配,不传则列出所有分支供选择) |
18
+
19
+ **使用场景:**
20
+
21
+ 当目标 worktree 的分支需要使用主分支的最新代码继续工作时,通过 `clawt sync` 将主分支最新代码合并到目标 worktree。在新架构下,sync 不再是为了解决 validate 冲突(因为不会冲突了),而是纯粹的「将主分支最新代码同步到目标 worktree」的操作。
22
+
23
+ **运行流程:**
24
+
25
+ 1. **主 worktree 校验** (2.1)
26
+ 2. **项目配置校验**:调用 `requireProjectConfig()` 确保项目已初始化(存在 `clawtMainWorkBranch` 配置)
27
+ 3. **确保在主工作分支上**:`handleSync` 在执行核心逻辑前,调用 `ensureOnMainWorkBranch()` 确保当前处于主工作分支上。sync 命令需要从主分支发起合并操作,因此必须保证当前分支状态正确。
28
+ 4. **解析目标 worktree**:根据 `-b` 参数解析目标 worktree,匹配策略如下:
29
+ - **未传 `-b` 参数**:
30
+ - 获取当前项目所有 worktree
31
+ - 无可用 worktree → 报错退出
32
+ - 仅 1 个 worktree → 直接使用,无需选择
33
+ - 多个 worktree → 通过交互式列表(Enquirer.Select)让用户选择
34
+ - **传了 `-b` 参数**:
35
+ 1. **精确匹配优先**:在 worktree 列表中查找分支名完全相同的 worktree,找到则直接使用
36
+ 2. **模糊匹配**(子串匹配,大小写不敏感):
37
+ - 唯一匹配 → 直接使用
38
+ - 多个匹配 → 通过交互式列表让用户从匹配结果中选择
39
+ 3. **无匹配** → 报错退出,并列出所有可用分支名
40
+ 5. 调用 `executeSyncForBranch(targetWorktreePath, branch)` 执行核心同步逻辑
41
+
42
+ #### `executeSyncForBranch` — sync 核心操作函数
43
+
44
+ `executeSyncForBranch(targetWorktreePath: string, branch: string): Promise<SyncResult>` 是从 `handleSync` 中抽取的核心同步逻辑(async 函数),不包含 worktree 解析交互,供 validate 等命令复用。
45
+
46
+ **接口定义:**
47
+
48
+ ```typescript
49
+ /** sync 核心操作的执行结果 */
50
+ export interface SyncResult {
51
+ /** 是否同步成功 */
52
+ success: boolean;
53
+ /** 是否存在合并冲突 */
54
+ hasConflict: boolean;
55
+ }
56
+ ```
57
+
58
+ **执行流程:**
59
+
60
+ 1. **获取主分支名**:通过项目级配置 `clawtMainWorkBranch` 获取主工作分支名(不再通过 `getCurrentBranch` 动态获取,因为在新架构下主 worktree 可能处于验证分支上)
61
+ 2. **自动保存未提交变更**:检查目标 worktree 是否有未提交修改
62
+ - 有修改 → 自动执行 `git add . && git commit -m "<AUTO_SAVE_COMMIT_MESSAGE>"` 保存变更(commit message 由常量 `AUTO_SAVE_COMMIT_MESSAGE` 定义,值为 `chore: auto-save before sync`,同时用于 merge 命令的 squash 检测)
63
+ - 无修改 → 跳过
64
+ 3. **在目标 worktree 中合并主分支**:
65
+ ```bash
66
+ cd ~/.clawt/worktrees/<project>/<branchName>
67
+ git merge <mainBranch>
68
+ ```
69
+ 4. **冲突处理**:
70
+ - **有冲突** → 输出警告,提示用户进入目标 worktree 手动解决:
71
+ ```
72
+ 合并存在冲突,请进入目标 worktree 手动解决:
73
+ cd ~/.clawt/worktrees/<project>/<branchName>
74
+ 解决冲突后执行 git add . && git merge --continue
75
+ clawt validate -b <branch> 验证变更
76
+ ```
77
+ - 返回 `{ success: false, hasConflict: true }`
78
+ - **无冲突** → 继续
79
+ 5. **清除 validate 快照**:合并成功后,如果该分支存在 validate 快照(`.tree` 和 `.head` 文件),自动删除(代码基础已变化,旧快照无效)
80
+ 6. **重建验证分支**(`rebuildValidateBranch`,async 函数):sync 将主分支合并到目标 worktree 后,目标分支的代码基点发生变化。为保持验证分支与目标分支基点一致,需要重建验证分支。
81
+ - 确保在主工作分支上创建验证分支,处理三种情况:
82
+ - **已在主工作分支上** → 直接重建
83
+ - **在验证分支上** → 验证分支修改可丢弃,清理工作区后自动切回主工作分支
84
+ - **在其他普通分支上** → 检查工作区是否干净,干净则直接切回主工作分支;不干净则交互处理(`handleDirtyWorkingDir`:reset / stash / exit)后切回
85
+ ```bash
86
+ # 情况 1:已在主工作分支上,无需切换
87
+
88
+ # 情况 2:在验证分支上,先清理工作区再切回主分支
89
+ git reset --hard
90
+ git clean -fd
91
+ git checkout <clawtMainWorkBranch>
92
+
93
+ # 情况 3:在其他分支上
94
+ # 如果工作区不干净,交互式处理(reset/stash/exit)
95
+ # 然后切回主工作分支
96
+ git checkout <clawtMainWorkBranch>
97
+
98
+ # 删除旧验证分支
99
+ git branch -D clawt-validate-<branchName>
100
+
101
+ # 基于当前主分支 HEAD 重新创建验证分支
102
+ git branch clawt-validate-<branchName>
103
+ ```
104
+ 7. **输出成功提示**,然后执行 `rebuildValidateBranch` 重建验证分支,再输出验证分支重建提示,最后返回 `{ success: true, hasConflict: false }`:
105
+ ```
106
+ ✓ 已将 <mainBranch> 的最新代码同步到 <branchName>
107
+ 验证分支 clawt-validate-<branchName> 已重建
108
+ ```
109
+
110
+ #### validate 中自动 sync 的联动
111
+
112
+ 当 validate 的 patch apply 失败(兜底场景)并触发自动 sync 时,sync 内部会自动重建验证分支,validate 流程结束后用户重新执行 validate 即可。
113
+
114
+ ---
@@ -0,0 +1,95 @@
1
+ ### 5.17 自动更新检查
2
+
3
+ CLI 在每次命令执行完毕后,根据配置项 `autoUpdate` 决定是否检查 npm registry 上的最新版本。当发现新版本时,以带边框的提示框在终端输出版本更新信息和升级命令。
4
+
5
+ #### 触发条件
6
+
7
+ - 配置项 `autoUpdate` 为 `true`(默认启用)
8
+ - 命令正常执行完毕后触发(在 `program.parseAsync()` 之后)
9
+
10
+ #### 检查流程
11
+
12
+ 1. 读取缓存文件 `~/.clawt/update-check.json`
13
+ 2. 判断缓存是否有效:
14
+ - 缓存不存在或解析失败 → 视为过期
15
+ - 缓存中的 `currentVersion` 与本地版本不一致 → 视为过期
16
+ - 距离上次检查超过 24 小时 → 视为过期
17
+ 3. **缓存有效**:直接使用缓存中的 `latestVersion` 与本地版本比较,有新版本则打印提示
18
+ 4. **缓存过期**:向 npm registry 发起 HTTPS 请求获取最新版本号(5 秒超时),更新缓存文件后判断并打印提示
19
+
20
+ #### 缓存文件
21
+
22
+ **路径:** `~/.clawt/update-check.json`
23
+
24
+ **结构:**
25
+
26
+ ```json
27
+ {
28
+ "lastCheck": 1709000000000,
29
+ "latestVersion": "2.18.0",
30
+ "currentVersion": "2.17.1"
31
+ }
32
+ ```
33
+
34
+ | 字段 | 类型 | 说明 |
35
+ | ---- | ---- | ---- |
36
+ | `lastCheck` | `number` | 上次检查时间戳(毫秒) |
37
+ | `latestVersion` | `string` | 从 registry 获取的最新版本号 |
38
+ | `currentVersion` | `string` | 检查时的本地版本号 |
39
+
40
+ #### 版本比较
41
+
42
+ 使用简易 semver 比较(不引入额外依赖),逐级比较 `major.minor.patch`:
43
+
44
+ - `latest > current` → 提示更新
45
+ - `latest <= current` → 不提示
46
+
47
+ #### 包管理器检测
48
+
49
+ 更新提示中会显示与用户安装方式匹配的升级命令。检测逻辑依次尝试:
50
+
51
+ 1. `pnpm list -g --depth=0 clawt` → 匹配则提示 `pnpm add -g clawt`
52
+ 2. `yarn global list --depth=0` → 输出含 `clawt` 则提示 `yarn global add clawt`
53
+ 3. 以上均未匹配 → 默认提示 `npm i -g clawt`
54
+
55
+ #### 提示框格式
56
+
57
+ 当检测到新版本时,输出带 Unicode 圆角边框的居中提示框:
58
+
59
+ ```
60
+ ╭──────────────────────────────────────────────╮
61
+ │ │
62
+ │ clawt 有新版本可用: 2.17.1 → 2.18.0 │
63
+ │ 执行 npm i -g clawt 进行更新 │
64
+ │ │
65
+ ╰──────────────────────────────────────────────╯
66
+ ```
67
+
68
+ 版本号和命令使用 chalk 着色:当前版本红色、最新版本绿色、更新命令青色。
69
+
70
+ #### 容错设计
71
+
72
+ 所有异常静默处理,不影响 CLI 正常功能:
73
+
74
+ - 网络请求失败或超时(5 秒) → 静默忽略
75
+ - registry 返回无效 JSON 或缺少 `version` 字段 → 静默忽略
76
+ - 缓存文件读写失败 → 静默忽略
77
+ - `checkForUpdates()` 入口函数的最外层 `try/catch` 确保任何未预期异常都不会中断 CLI
78
+
79
+ #### 常量定义
80
+
81
+ | 常量 | 值 | 位置 |
82
+ | ---- | -- | ---- |
83
+ | `UPDATE_CHECK_INTERVAL_MS` | `86400000`(24 小时) | `src/constants/update.ts` |
84
+ | `NPM_REGISTRY_URL` | `https://registry.npmjs.org/clawt/latest` | `src/constants/update.ts` |
85
+ | `NPM_REGISTRY_TIMEOUT_MS` | `5000` | `src/constants/update.ts` |
86
+ | `PACKAGE_NAME` | `clawt` | `src/constants/update.ts` |
87
+ | `UPDATE_CHECK_PATH` | `~/.clawt/update-check.json` | `src/constants/paths.ts` |
88
+
89
+ #### 实现说明
90
+
91
+ - 入口函数:`checkForUpdates()`(在 `src/utils/update-checker.ts`)
92
+ - 消息常量:`UPDATE_MESSAGES`、`UPDATE_COMMANDS`(在 `src/constants/messages/update.ts`)
93
+ - 入口调用点:`src/index.ts` 的 `main()` 异步函数中,`program.parseAsync()` 之后根据 `config.autoUpdate` 条件调用
94
+
95
+ ---