clawt 3.10.4 → 3.10.6

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 (48) hide show
  1. package/AGENTS.md +16 -0
  2. package/dist/index.js +228 -86
  3. package/dist/postinstall.js +27 -0
  4. package/docs/create.md +1 -0
  5. package/docs/list.md +21 -10
  6. package/docs/merge.md +1 -0
  7. package/docs/remove.md +2 -0
  8. package/docs/spec.md +4 -1
  9. package/docs/status.md +9 -1
  10. package/docs/superpowers/findings/2026-06-01-sync-validate-diverged-findings.md +203 -0
  11. package/docs/superpowers/findings/2026-06-09-worktree-base-branch-findings.md +58 -0
  12. package/docs/superpowers/plans/2026-06-01-validate-ignored-files-conflict.md +412 -0
  13. package/docs/superpowers/plans/2026-06-09-worktree-base-branch.md +386 -0
  14. package/docs/superpowers/specs/2026-06-01-validate-ignored-files-conflict-design.md +76 -0
  15. package/docs/superpowers/specs/2026-06-09-worktree-base-branch-design.md +169 -0
  16. package/docs/validate.md +42 -5
  17. package/package.json +1 -1
  18. package/src/commands/list.ts +5 -3
  19. package/src/commands/merge.ts +1 -1
  20. package/src/commands/remove.ts +3 -0
  21. package/src/commands/status.ts +5 -0
  22. package/src/constants/messages/validate.ts +17 -0
  23. package/src/types/status.ts +2 -0
  24. package/src/types/worktree.ts +12 -0
  25. package/src/utils/formatter.ts +22 -0
  26. package/src/utils/git-core.ts +23 -0
  27. package/src/utils/index.ts +4 -2
  28. package/src/utils/interactive-panel-render.ts +6 -3
  29. package/src/utils/validate-core.ts +52 -0
  30. package/src/utils/worktree-metadata.ts +82 -0
  31. package/src/utils/worktree.ts +29 -10
  32. package/tests/helpers/fixtures.ts +1 -0
  33. package/tests/unit/commands/cover-validate.test.ts +4 -4
  34. package/tests/unit/commands/create.test.ts +3 -3
  35. package/tests/unit/commands/list.test.ts +66 -3
  36. package/tests/unit/commands/merge.test.ts +1 -1
  37. package/tests/unit/commands/remove.test.ts +24 -18
  38. package/tests/unit/commands/resume.test.ts +21 -21
  39. package/tests/unit/commands/run.test.ts +17 -17
  40. package/tests/unit/commands/status.test.ts +85 -10
  41. package/tests/unit/commands/sync.test.ts +4 -4
  42. package/tests/unit/commands/validate.test.ts +1 -1
  43. package/tests/unit/utils/git-core.test.ts +43 -0
  44. package/tests/unit/utils/interactive-panel-render.test.ts +124 -0
  45. package/tests/unit/utils/validate-core.test.ts +60 -0
  46. package/tests/unit/utils/worktree-matcher.test.ts +2 -2
  47. package/tests/unit/utils/worktree-metadata.test.ts +91 -0
  48. package/tests/unit/utils/worktree.test.ts +65 -0
@@ -726,6 +726,33 @@ var VALIDATE_MESSAGES_I18N = {
726
726
  "zh-CN": (branch) => `\u53D8\u66F4\u8FC1\u79FB\u5931\u8D25\uFF1A\u76EE\u6807\u5206\u652F\u4E0E\u4E3B\u5206\u652F\u5DEE\u5F02\u8FC7\u5927
727
727
  \u8BF7\u5148\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`
728
728
  },
729
+ /** validate 检测到被 .gitignore 忽略的残留文件冲突 */
730
+ VALIDATE_IGNORED_FILES_CONFLICT: {
731
+ en: (files, cleanCommands) => {
732
+ const maxDisplay = 10;
733
+ const displayed = files.slice(0, maxDisplay).map((f) => ` - ${f}`).join("\n");
734
+ const more = files.length > maxDisplay ? `
735
+ ...(${files.length} files total)` : "";
736
+ const cmds = cleanCommands.map((c) => ` ${c}`).join("\n");
737
+ return `Ignored files left in main worktree are blocking patch apply:
738
+ ${displayed}${more}
739
+
740
+ Please clean up manually and retry:
741
+ ${cmds}`;
742
+ },
743
+ "zh-CN": (files, cleanCommands) => {
744
+ const maxDisplay = 10;
745
+ const displayed = files.slice(0, maxDisplay).map((f) => ` - ${f}`).join("\n");
746
+ const more = files.length > maxDisplay ? `
747
+ ...\uFF08\u5171 ${files.length} \u4E2A\u6587\u4EF6\uFF09` : "";
748
+ const cmds = cleanCommands.map((c) => ` ${c}`).join("\n");
749
+ return `\u68C0\u6D4B\u5230\u88AB .gitignore \u5FFD\u7565\u7684\u6587\u4EF6\u6B8B\u7559\u5728\u4E3B worktree \u4E2D\uFF0C\u5BFC\u81F4\u53D8\u66F4\u65E0\u6CD5\u5E94\u7528\uFF1A
750
+ ${displayed}${more}
751
+
752
+ \u8BF7\u624B\u52A8\u6E05\u7406\u540E\u91CD\u8BD5\uFF1A
753
+ ${cmds}`;
754
+ }
755
+ },
729
756
  /** validate 无可用 worktree */
730
757
  VALIDATE_NO_WORKTREES: {
731
758
  en: "No worktrees available, please create one with clawt run or clawt create first",
package/docs/create.md CHANGED
@@ -50,6 +50,7 @@ clawt create -b <branchName> [-n <count>] [--no-post-create]
50
50
  - 若 `n > 1`:校验 `branchName-1` 到 `branchName-n`
51
51
  - 所有分支名在创建任何 worktree **之前**完成全部校验
52
52
  8. **批量创建 worktree + 验证分支**
53
+ - 创建前记录当前所在分支为来源分支(`baseBranch`),保存到 `~/.clawt/projects/<projectName>/worktrees/<branchName>.json`
53
54
  - 若 `n = 1`:
54
55
  ```bash
55
56
  git worktree add -b <branchName> ~/.clawt/worktrees/<project>/<branchName>
package/docs/list.md CHANGED
@@ -10,7 +10,7 @@ clawt list [--json]
10
10
 
11
11
  | 参数 | 必填 | 说明 |
12
12
  | -------- | ---- | ---------------------------------------- |
13
- | `--json` | 否 | 以 JSON 格式输出(仅包含 path 和 branch) |
13
+ | `--json` | 否 | 以 JSON 格式输出(包含 path、branchbaseBranch) |
14
14
 
15
15
  **运行流程:**
16
16
 
@@ -18,26 +18,26 @@ clawt list [--json]
18
18
  2. **获取项目名** (2.2)
19
19
  3. 获取项目的所有 worktree 列表(扫描 `~/.clawt/worktrees/<project>/` 目录,与 `git worktree list` 交叉验证)
20
20
  4. 根据 `--json` 选项决定输出格式:
21
- - 指定 `--json` → 以 JSON 格式输出(仅包含 path 和 branch
22
- - 未指定 → 以文本格式输出(包含变更状态信息)
21
+ - 指定 `--json` → 以 JSON 格式输出(包含 path、branchbaseBranch
22
+ - 未指定 → 以文本格式输出(包含变更状态信息和来源分支)
23
23
 
24
24
  **文本输出格式(默认):**
25
25
 
26
- 每个 worktree 会显示路径、分支名和变更状态。每个 worktree 条目下方额外显示一行状态信息(提交数、变更行数、未提交修改),各条目之间以空行分隔。如果某个 worktree 处于空闲状态(0 个提交、无变更、无未提交修改),其路径会以橙色高亮显示,方便用户快速识别可能需要清理或还未开始工作的 worktree。
26
+ 每个 worktree 会显示路径、分支名、来源分支和变更状态。来源分支以 `<- <branchName>` 形式显示在分支名后,无元数据时显示 `<- 未记录`。每个 worktree 条目下方额外显示一行状态信息(提交数、变更行数、未提交修改),各条目之间以空行分隔。如果某个 worktree 处于空闲状态(0 个提交、无变更、无未提交修改),其路径会以橙色高亮显示,方便用户快速识别可能需要清理或还未开始工作的 worktree。
27
27
 
28
28
  ```
29
29
  当前项目: main-project
30
30
 
31
- ~/.clawt/worktrees/main-project/feature-scheme-1 [feature-scheme-1]
31
+ ~/.clawt/worktrees/main-project/feature-scheme-1 [feature-scheme-1] <- main
32
32
  3 个提交 +120 -30 (未提交修改)
33
33
 
34
- ~/.clawt/worktrees/main-project/feature-scheme-2 [feature-scheme-2]
34
+ ~/.clawt/worktrees/main-project/feature-scheme-2 [feature-scheme-2] <- test
35
35
  1 个提交 +45 -10
36
36
 
37
- ~/.clawt/worktrees/main-project/feature-scheme-3 [feature-scheme-3] ← 橙色路径(空闲)
37
+ ~/.clawt/worktrees/main-project/feature-scheme-3 [feature-scheme-3] <- 未记录 ← 橙色路径(空闲)
38
38
  0 个提交 无变更
39
39
 
40
- ~/.clawt/worktrees/main-project/bugfix-login [bugfix-login]
40
+ ~/.clawt/worktrees/main-project/bugfix-login [bugfix-login] <- main
41
41
  2 个提交 +80 -25
42
42
 
43
43
  共 4 个 worktree
@@ -60,14 +60,25 @@ clawt list [--json]
60
60
  "worktrees": [
61
61
  {
62
62
  "path": "~/.clawt/worktrees/main-project/feature-scheme-1",
63
- "branch": "feature-scheme-1"
63
+ "branch": "feature-scheme-1",
64
+ "baseBranch": "main"
64
65
  },
65
66
  {
66
67
  "path": "~/.clawt/worktrees/main-project/feature-scheme-2",
67
- "branch": "feature-scheme-2"
68
+ "branch": "feature-scheme-2",
69
+ "baseBranch": "test"
70
+ },
71
+ {
72
+ "path": "~/.clawt/worktrees/main-project/legacy",
73
+ "branch": "legacy",
74
+ "baseBranch": null
68
75
  }
69
76
  ]
70
77
  }
71
78
  ```
72
79
 
80
+ `baseBranch` 字段说明:
81
+ - 有值时:表示创建 worktree 时所在的真实当前分支
82
+ - `null`:表示历史 worktree 无元数据记录(clawt 不会通过 git 推断,避免误导)
83
+
73
84
  ---
package/docs/merge.md CHANGED
@@ -144,6 +144,7 @@ clawt merge [-m <commitMessage>]
144
144
  git branch -D clawt-validate-<branchName>
145
145
  # 修剪 worktree 引用
146
146
  git worktree prune
147
+ # 清理该分支的来源分支元数据文件
147
148
  # 如果项目 worktree 目录为空,则清理空目录
148
149
  ```
149
150
  - 输出清理成功提示:`✓ 已清理 worktree 和分支: <branchName>`
package/docs/remove.md CHANGED
@@ -70,6 +70,7 @@ git branch -D <branchName>
70
70
  # 无条件删除验证分支(不受用户确认控制,存在则删除)
71
71
  git branch -D clawt-validate-<branchName>
72
72
  # 无条件清理该分支对应的 validate 快照
73
+ # 无条件清理该分支的元数据文件(~/.clawt/projects/<projectName>/worktrees/<branchName>.json)
73
74
  ```
74
75
 
75
76
  8. 如果使用 `--all` 模式,额外清理整个项目的 validate 快照目录。
@@ -86,5 +87,6 @@ git branch -D clawt-validate-<branchName>
86
87
  - 分支解析逻辑复用公共模块 `resolveTargetWorktrees`(`src/utils/worktree-matcher.ts`)
87
88
  - 验证分支删除通过 `deleteValidateBranch`(`src/utils/validate-branch.ts`),内部判断分支是否存在后才执行删除
88
89
  - 快照清理通过 `removeSnapshot` 和 `removeProjectSnapshots`(`src/utils/validate-snapshot.ts`)
90
+ - 元数据清理通过 `removeWorktreeMetadata`(`src/utils/worktree-metadata.ts`),删除 `~/.clawt/projects/<projectName>/worktrees/<branchName>.json`,失败时仅记录日志不抛异常(best-effort 语义)
89
91
 
90
92
  ---
package/docs/spec.md CHANGED
@@ -304,7 +304,10 @@ async function interactiveConfigEditor<T extends object>(
304
304
  │ ├── <branchName>.staged # 每个分支一个 staged tree hash 快照文件(存储 validate 结束时暂存区对应的 tree hash,用于无变更时恢复)
305
305
  │ └── ...
306
306
  ├── projects/<project-name>/ # 项目级配置目录
307
- └── config.json # 项目级配置(含 clawtMainWorkBranch)
307
+ ├── config.json # 项目级配置(含 clawtMainWorkBranch)
308
+ │ └── worktrees/ # worktree 元数据目录(记录来源分支等信息)
309
+ │ ├── <branchName>.json # 每个 worktree 的元数据文件(含 branch、baseBranch、createdAt)
310
+ │ └── ...
308
311
  └── worktrees/ # 所有 worktree 的统一存放目录
309
312
  └── <project-name>/ # 以项目名分组
310
313
  ├── <branchName>/ # n=1 时直接使用分支名
package/docs/status.md CHANGED
@@ -29,13 +29,14 @@ clawt status [--json] [-i | --interactive]
29
29
  - 加载项目配置(`loadProjectConfig()`),读取配置的主工作分支名(`clawtMainWorkBranch`)
30
30
  - 检测配置的主工作分支是否存在(`checkBranchExists()`)
31
31
  3. **收集各 worktree 详细状态**:
32
- - 获取项目所有 worktree(`getProjectWorktrees()`)
32
+ - 获取项目所有 worktree(`getProjectWorktrees()`),每个 worktree 携带 `baseBranch`(来源分支,无元数据时为 null)
33
33
  - 对每个 worktree 收集以下信息:
34
34
  - **变更状态**(优先级:合并冲突 > 未提交修改 > 已提交 > 无变更)
35
35
  - **行数差异**(新增/删除行数,通过 `getDiffStat()` 获取)
36
36
  - **提交差异**(相对于主分支的领先提交数 `getCommitCountAhead()` 和落后提交数 `getCommitCountBehind()`)
37
37
  - **快照时间**(validate 快照文件的 mtime,通过 `getSnapshotModifiedTime()` 获取,返回 ISO 8601 时间字符串或 null)
38
38
  - **创建时间**(通过 `getWorktreeCreatedTime()` 从文件系统 birthtime 获取 worktree 目录的创建时间)
39
+ - **来源分支**(`baseBranch`,从 `~/.clawt/projects/<projectName>/worktrees/<branchName>.json` 读取,无元数据时显示"未记录")
39
40
  4. **收集 validate 快照摘要**:
40
41
  - 通过 `getProjectSnapshotBranches()` 扫描快照目录下的 `.tree` 文件获取所有存在快照的分支名
41
42
  - 统计快照总数和孤立快照数(对应 worktree 已不存在的快照)
@@ -73,6 +74,7 @@ clawt status [--json] [-i | --interactive]
73
74
  +120 -30
74
75
  3 个本地提交
75
76
  与主分支同步
77
+ 来源分支: main
76
78
  创建于 3 天前
77
79
  上次验证: 2 小时前
78
80
 
@@ -80,6 +82,7 @@ clawt status [--json] [-i | --interactive]
80
82
  +45 -10
81
83
  1 个本地提交
82
84
  落后主分支 2 个提交
85
+ 来源分支: test
83
86
  创建于 1 天前
84
87
  ✗ 未验证
85
88
 
@@ -105,6 +108,7 @@ clawt status [--json] [-i | --interactive]
105
108
  - 行数变更(`+N -N`):仅在有变更时展示,独立一行
106
109
  - 本地提交数(`N 个本地提交`):仅在有提交时展示,独立一行(黄色)
107
110
  - 与主分支同步状态:始终展示,独立一行(落后时显示黄色,同步时显示绿色)
111
+ - 来源分支(`来源分支: <branchName>`):始终展示,独立一行(灰色);无元数据时显示 `来源分支: 未记录`
108
112
 
109
113
  **创建时间行:**
110
114
 
@@ -139,6 +143,7 @@ clawt status [--json] [-i | --interactive]
139
143
  {
140
144
  "path": "~/.clawt/worktrees/main-project/feature-login",
141
145
  "branch": "feature-login",
146
+ "baseBranch": "main",
142
147
  "changeStatus": "committed",
143
148
  "commitsAhead": 3,
144
149
  "commitsBehind": 0,
@@ -214,6 +219,7 @@ clawt status [--json] [-i | --interactive]
214
219
  +120 -30
215
220
  3 个本地提交
216
221
  与主分支同步
222
+ 来源分支: main
217
223
  创建于 3 天前
218
224
  上次验证: 2 小时前
219
225
 
@@ -221,6 +227,7 @@ clawt status [--json] [-i | --interactive]
221
227
  +45 -10
222
228
  1 个本地提交
223
229
  落后主分支 2 个提交
230
+ 来源分支: test
224
231
  创建于 1 天前
225
232
  ✗ 未验证
226
233
 
@@ -228,6 +235,7 @@ clawt status [--json] [-i | --interactive]
228
235
 
229
236
  fix-bug [无变更]
230
237
  与主分支同步
238
+ 来源分支: 未记录
231
239
  创建于 5 天前
232
240
  ✗ 未验证
233
241
 
@@ -0,0 +1,203 @@
1
+ # sync 后 validate 仍然失败 — 调查记录
2
+
3
+ **日期:** 2026-06-01
4
+
5
+ ---
6
+
7
+ ## 现象描述
8
+
9
+ 用户在交互式面板(`clawt status`)中对目标分支执行 validate,patch apply 失败,提示:
10
+
11
+ ```
12
+ ⚠ Change migration failed: target branch has diverged too far from main
13
+ Please run clawt sync -b <target-branch> first, then retry
14
+ ```
15
+
16
+ 用户选择自动 sync(或手动执行 `clawt sync -b <target-branch>`),sync 成功:
17
+
18
+ ```
19
+ ✓ Synced latest code from <main-branch> to <target-branch>
20
+ Validation branch clawt-validate-<target-branch> has been rebuilt
21
+ ```
22
+
23
+ 但再次执行 validate 时,仍然报同样的错误。
24
+
25
+ ---
26
+
27
+ ## 调查过程
28
+
29
+ ### 第一步:确认分支状态
30
+
31
+ - 主工作分支:HEAD 指向最新 commit
32
+ - 验证分支:与主分支 HEAD 一致(sync 后已重建)
33
+ - 目标分支:包含多次 merge commit,已合并主分支最新代码
34
+
35
+ **结论:** sync 已成功,验证分支已正确重建,分支状态本身没有问题。
36
+
37
+ ### 第二步:模拟 patch apply
38
+
39
+ 执行三点 diff 并尝试 apply:
40
+
41
+ ```bash
42
+ git diff clawt-validate-<target-branch>...<target-branch> --binary | git apply --check -
43
+ ```
44
+
45
+ 输出:
46
+
47
+ ```
48
+ 错误:<ignored-dir>/findings/some-findings.md:已经存在于工作区中
49
+ 错误:<ignored-dir>/plans/some-plan.md:已经存在于工作区中
50
+ ...(共 N 个文件)
51
+ ```
52
+
53
+ **关键发现:** patch 失败的原因不是分支差异过大,而是目标分支中跟踪的某些文件在主 worktree 工作目录中已经物理存在。
54
+
55
+ ### 第三步:追溯文件来源
56
+
57
+ 检查主 worktree 工作目录:
58
+
59
+ ```bash
60
+ ls <ignored-dir>/findings/
61
+ # some-findings.md ← 物理存在
62
+ # ...(共多个文件)
63
+ ```
64
+
65
+ 检查 `git status`:
66
+
67
+ ```
68
+ 无文件要提交,干净的工作区
69
+ ```
70
+
71
+ **为什么这些文件物理存在但 git status 不显示?**
72
+
73
+ 检查 `.gitignore`:
74
+
75
+ ```bash
76
+ cat .gitignore | grep <ignored-dir>
77
+ # <ignored-dir>/ ← 被忽略!
78
+ ```
79
+
80
+ **结论:** 该目录在主分支的 `.gitignore` 中,这些文件是**被忽略的未跟踪文件**,`git status` 不会显示它们。
81
+
82
+ ### 第四步:追溯文件如何进入主 worktree
83
+
84
+ 这些文件是由**之前的 validate 操作**创建的:
85
+
86
+ 1. validate 通过 `git diff HEAD...branch --binary | git apply -` 将目标分支的变更(包括被忽略目录下的文件)应用到主 worktree 工作目录
87
+ 2. 目标分支跟踪了这些文件(尽管 `.gitignore` 中有该目录,但 AI Agent 在 worktree 中用 `git add -f` 强制添加了它们)
88
+ 3. validate 完成后,这些文件作为**未跟踪文件**留在主 worktree 工作目录中
89
+
90
+ ### 第五步:追溯为什么清理无效
91
+
92
+ validate 的清理逻辑(`handleIncrementalValidate` 步骤 2 和 `handleDirtyWorkingDir` 的 reset 选项)使用:
93
+
94
+ ```bash
95
+ git reset --hard # 只影响已跟踪文件
96
+ git clean -fd # 删除未跟踪文件,但**不删除被忽略的文件**
97
+ ```
98
+
99
+ `git clean -fd` 的行为:
100
+ - 删除未跟踪且未被忽略的文件 ✓
101
+ - **跳过**在 `.gitignore` 中的文件 ✗
102
+
103
+ 要删除被忽略的文件,需要 `git clean -fdx`(`-x` 表示包括被忽略的文件)。
104
+
105
+ ### 第六步:确认 sync 为什么不解决问题
106
+
107
+ `executeSyncForBranch` 的操作:
108
+ 1. 检查目标 worktree 未提交变更 → 自动 commit
109
+ 2. 在目标 worktree 中 `git merge <main-branch>`
110
+ 3. 重建验证分支
111
+
112
+ **sync 完全不清理主 worktree 的工作目录**,因此那些被忽略的残留文件仍然存在。
113
+
114
+ ---
115
+
116
+ ## 根本原因
117
+
118
+ **完整因果链:**
119
+
120
+ ```
121
+ 上游 commit 将某目录加入 .gitignore 并从 git 跟踪中移除
122
+
123
+ 目标分支仍然跟踪着自己新增的文件(AI Agent 用 git add -f 强制提交)
124
+
125
+ validate 通过 patch apply 将这些文件创建到主 worktree 工作目录
126
+
127
+ validate 结束后的清理(git clean -fd)无法删除这些文件(因为在 .gitignore 中)
128
+
129
+ 文件作为"被忽略的未跟踪文件"永久残留在主 worktree
130
+
131
+ 下次 validate 的 patch apply 试图创建同名文件 → "已经存在于工作区中" → 失败
132
+
133
+ auto-sync 不清理主 worktree 工作目录 → 问题持续存在(死循环)
134
+ ```
135
+
136
+ **核心矛盾:**
137
+
138
+ - 被忽略目录在主分支 `.gitignore` 中 → `git clean -fd` 不删除
139
+ - 被忽略目录在目标分支被跟踪 → patch 包含这些文件 → apply 时与物理存在的文件冲突
140
+
141
+ ---
142
+
143
+ ## 影响范围
144
+
145
+ 此 bug 在以下条件同时满足时触发:
146
+
147
+ 1. 目标分支跟踪了某些在 `.gitignore` 中的文件(通常是 AI Agent 用 `git add -f` 强制添加)
148
+ 2. 主分支的 `.gitignore` 包含对应路径
149
+ 3. 至少执行过一次 validate(使文件通过 patch 进入主 worktree)
150
+ 4. 后续再次执行 validate(patch apply 因文件已存在而失败)
151
+
152
+ ---
153
+
154
+ ## 技术决策记录
155
+
156
+ | 假设 | 验证结果 |
157
+ |------|----------|
158
+ | 分支确实 diverged(差异过大) | ❌ 排除:三点 diff 正常,merge-base 正确 |
159
+ | 验证分支未正确重建 | ❌ 排除:验证分支与主分支 HEAD 一致 |
160
+ | sync 未成功执行 | ❌ 排除:sync 成功,目标分支已合并主分支 |
161
+ | patch 内容与主分支冲突 | ❌ 排除:不是内容冲突,是文件已存在 |
162
+ | **被忽略文件残留导致 patch apply 失败** | ✅ 确认:`git apply --check` 明确报 "已经存在于工作区中" |
163
+
164
+ ---
165
+
166
+ ## Brainstorming 决策(2026-06-01)
167
+
168
+ ### 方案评估
169
+
170
+ | 方案 | 安全性 | 便利性 | 误删风险 | 代码复杂度 |
171
+ |------|--------|--------|----------|------------|
172
+ | A:检测 + 提示用户手动清理 | 高 | 低 | 无 | 低 |
173
+ | B:检测 + 自动清理后继续 | 中 | 高 | 有 | 略高 |
174
+
175
+ ### 最终决策
176
+
177
+ **选择方案 A(检测 + 提示用户手动清理)**
178
+
179
+ 理由:
180
+ 1. 这类冲突本质上是项目 `.gitignore` 配置与分支跟踪状态不一致导致的,属于需要用户知情的项目级决策
181
+ 2. 不应该由工具静默处理,用户需要知道哪些文件被清理了
182
+ 3. 实现简单,风险为零
183
+
184
+ ### 实现方案
185
+
186
+ 1. 新增 `gitCheckIgnored(paths, cwd)` — 批量检测文件是否被 `.gitignore` 忽略
187
+ 2. 新增 `detectIgnoredFilesInPatch(branchName, mainWorktreePath)` — 检测 patch 中的幽灵文件
188
+ 3. 修改 `migrateChangesViaPatch` — apply 前调用检测函数,有冲突则输出提示并返回失败
189
+ 4. 新增消息常量 `VALIDATE_IGNORED_FILES_CONFLICT`(双语)
190
+
191
+ ### 提示格式
192
+
193
+ ```
194
+ ⚠ 检测到被 .gitignore 忽略的文件残留在主 worktree 中,导致变更无法应用:
195
+ - <ignored-dir>/findings/some-findings.md
196
+ - <ignored-dir>/plans/some-plan.md
197
+ ...(共 N 个文件)
198
+
199
+ 请手动清理后重试:
200
+ git clean -fdx <ignored-dir>/
201
+ ```
202
+
203
+ 清理命令按冲突文件的直接父目录去重生成。
@@ -0,0 +1,58 @@
1
+ # 发现与决策
2
+
3
+ ## 需求
4
+ - 为 `clawt status`、`clawt status -i`、`clawt list` 增加当前 worktree/branch 的来源原始分支展示。
5
+ - 目标是避免把基于 `test` 等分支创建的 worktree 误合并到 `master` 等错误目标分支。
6
+ - 优先通过 git 信息识别来源分支;如果不可行,则在 `~/.clawt/` 的合适位置记录。
7
+ - 用户确认来源语义为:记录创建 worktree 时所在的真实当前分支,例如当时在 `test` 上创建就记录 `test`。
8
+
9
+ ## 研究发现
10
+ - `src/commands/status.ts` 的 `collectStatus()` 通过 `getProjectWorktrees()` 取得 worktree,再为每个 worktree 收集提交差异、porcelain、diff 统计、创建时间和 validate 快照。
11
+ - `src/commands/list.ts` 文本输出目前展示 `path [branch]`,JSON 输出只包含 `path` 和 `branch`。
12
+ - `src/utils/worktree.ts` 的 `getProjectWorktrees()` 只从 `~/.clawt/worktrees/<project>/` 目录名和 `git worktree list` 交叉验证得到 path/branch,没有来源分支字段。
13
+ - `src/utils/git-branch.ts` 的 `getCommitDivergenceAsync(branchName)` 当前使用 `HEAD...branchName`,在主 worktree 当前分支作为基准时有效,但不是每个 worktree 创建时的来源分支。
14
+ - `src/commands/create.ts` 通过 `createWorktrees(options.branch, count)` 创建 worktree;前置检查由 `PRE_CHECK_CREATE` 控制,文档说明 create 应确保当前在 `clawtMainWorkBranch` 上。
15
+ - 项目已有 `src/constants/paths.ts`,其中 `PROJECTS_CONFIG_DIR` 可复用为 worktree metadata 的项目级根目录。
16
+ - `src/constants/pre-checks.ts` 中 `PRE_CHECK_CREATE` 包含 `ensureOnClawtMainWorkBranch: true`,因此当前 `clawt create` 会尽量从项目配置主分支创建。
17
+ - `src/utils/project-config.ts` 将项目配置放在 `~/.clawt/projects/<projectName>/config.json`,但这是项目级单份配置,不适合直接塞大量 worktree 条目。
18
+ - `src/utils/interactive-panel-render.ts` 的 `renderWorktreeBlock()` 负责 `status -i` 每个 worktree 条目的显示,需要与文本版 `printWorktreeItem()` 保持字段一致。
19
+ - `tests/unit/commands/status.test.ts`、`tests/unit/commands/list.test.ts`、`tests/unit/utils/worktree.test.ts` 已覆盖 JSON 输出、文本渲染调用和 worktree 创建/解析,是本功能主要测试入口。
20
+ - 设计确认后,范围限定为:创建时记录 metadata,`status` / `status -i` / `list` 展示;不新增历史回填命令,不修改 merge 目标逻辑。
21
+
22
+ ## 技术决策
23
+ | 决策 | 理由 |
24
+ |------|------|
25
+ | 优先设计创建时记录来源分支 | Git worktree/list/reflog 不能稳定表达“当初从哪个业务分支创建”,尤其分支移动、merge、rebase 后事后推断容易误判 |
26
+ | 来源分支元数据放在 `~/.clawt/` 项目维度目录下 | 用户接受无法从 git 稳定获取时在 `~/.clawt/` 记录;现有 `WORKTREES_DIR` 和 `PROJECTS_CONFIG_DIR` 都在该目录下 |
27
+ | 展示逻辑应复用同一个来源解析函数 | `status`、`status -i`、`list` 都需要相同字段,避免三个命令各自拼装导致不一致 |
28
+ | 历史 worktree 缺失 metadata 时只显示未记录 | 只记录新建 worktree 会让旧 worktree 没有来源数据;显示未记录比不可靠推断更安全 |
29
+ | 来源分支定义为创建瞬间的当前分支 | 用户明确选择此语义;它能区分从 `master`、`test` 等不同分支创建的 worktree |
30
+ | 元数据存放在 `~/.clawt/projects/<projectName>/worktrees/<branchName>.json` | 用户认可该路径;它属于项目级动态元数据,不污染项目配置 `config.json`,且单分支单文件便于局部清理 |
31
+ | 历史 worktree 缺失 metadata 时显示未记录 | 不用不可靠 git 推断伪造来源,避免给用户错误安全感 |
32
+ | 不新增历史回填命令 | 本次目标是新增展示和新建记录能力,回填属于后续可独立设计的增强功能 |
33
+
34
+ ## 遇到的问题
35
+ | 问题 | 解决方案 |
36
+ |------|---------|
37
+ | Git 无法稳定反推出创建时原始分支 | 在创建 worktree 时写入 `~/.clawt/` 元数据,展示时读取;历史数据用回退策略标记 |
38
+ | `status -i` 和普通文本输出分别渲染 | 在状态数据层新增来源字段,让两个渲染器只负责显示 |
39
+ | `config.json` 不适合存储 worktree 来源 | 使用项目目录下的 `worktrees/<branch>.json` 存储动态元数据,保持配置与运行元数据分离 |
40
+
41
+ ## 资源
42
+ - `src/commands/status.ts`
43
+ - `src/commands/list.ts`
44
+ - `src/commands/create.ts`
45
+ - `src/utils/git-worktree.ts`
46
+ - `src/utils/worktree.ts`
47
+ - `src/utils/git-branch.ts`
48
+ - `src/constants/paths.ts`
49
+ - `src/types/status.ts`
50
+ - `src/types/worktree.ts`
51
+
52
+ ## 视觉 / 浏览器发现
53
+ <!-- 每执行 2 次查看/浏览器操作后必须更新此部分 -->
54
+ <!-- 多模态内容必须立即以文本形式记录 -->
55
+ - 本任务暂无视觉内容。
56
+
57
+ ---
58
+ *每执行 2 次查看/浏览器/搜索操作后更新此文件*