Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1ec7526
docs(worktree): update design doc — split Phase C/D, add Future section
LaZzyMan May 15, 2026
4088739
docs(worktree): add Phase C implementation plan
LaZzyMan May 15, 2026
b68c2b2
docs(worktree): update Phase C plan after claude-code comparison
LaZzyMan May 15, 2026
82181c5
feat(worktree): add WorktreeSession sidecar storage
LaZzyMan May 15, 2026
1164338
feat(worktree): configure core.hooksPath after worktree creation
LaZzyMan May 15, 2026
806521a
feat(worktree): persist WorktreeSession sidecar in EnterWorktreeTool
LaZzyMan May 15, 2026
185a6c9
feat(worktree): clear WorktreeSession sidecar in ExitWorktreeTool
LaZzyMan May 15, 2026
0e90902
feat(worktree): expose active worktree via useWorktreeSession + UIState
LaZzyMan May 15, 2026
3ce2811
feat(worktree): show active worktree in Footer + StatusLine payload
LaZzyMan May 15, 2026
783c70d
feat(worktree): inject context hint on --resume when worktree is active
LaZzyMan May 15, 2026
66c91da
feat(worktree): add WorktreeExitDialog with dirty-state inspection
LaZzyMan May 15, 2026
2c2e1ce
docs(worktree): add Phase C E2E test plan
LaZzyMan May 15, 2026
97afde7
docs(worktree): fix E2E test plan sidecar path + jq selector
LaZzyMan May 15, 2026
e847bfc
fix(worktree): add showWorktreeExitDialog to dialogsVisible
LaZzyMan May 15, 2026
ada0837
feat(worktree): extend --resume context restore to headless + ACP modes
LaZzyMan May 15, 2026
b16adb5
test(worktree): add ACP-mode integration tests for --resume context
LaZzyMan May 15, 2026
c1b8006
Merge remote-tracking branch 'origin/main' into lazzy/romantic-burnel…
LaZzyMan May 15, 2026
2ca730e
docs(worktree): clarify hooksPath comment + pendingWorktreeNotice one…
LaZzyMan May 15, 2026
20cea8f
fix(test): add getResumedSessionData to nonInteractiveCli mock Config
LaZzyMan May 15, 2026
b8c2a5d
Merge remote-tracking branch 'origin/main' into lazzy/romantic-burnel…
LaZzyMan May 18, 2026
38db00f
fix(worktree): address PR #4174 reviewer findings
LaZzyMan May 18, 2026
c0c0e71
fix(test): correct linter-mangled imports in useWorktreeSession.test
LaZzyMan May 18, 2026
ce8ed08
fix(test): normalize path separators for Windows in worktree session …
LaZzyMan May 18, 2026
d4e921a
fix(worktree): address PR #4174 round 4 findings
LaZzyMan May 18, 2026
80f9cb4
fix(worktree): address PR #4174 round 5 findings
LaZzyMan May 18, 2026
6af53ac
chore(worktree): regenerate settings.schema.json for hideBuiltinWorkt…
LaZzyMan May 18, 2026
7943487
fix(worktree): address PR #4174 round 6 findings
LaZzyMan May 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 95 additions & 34 deletions docs/design/worktree.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ qwen-code 目前仅有面向 Arena 多模型对比场景的内部 worktree 实

## 现状对比

| 功能 | qwen-code | claude-code |
| --------------------------------- | --------------- | ----------- |
| `EnterWorktree` 工具 | | ✅ |
| `ExitWorktree` 工具 | | ✅ |
| AgentTool `isolation: 'worktree'` | | ✅ |
| worktree 会话状态持久化与恢复 | ❌ | ✅ |
| 过期 worktree 自动清理 | ❌ | ✅ |
| Post-creation setup(hooks 配置) | ❌ | ✅ |
| StatusLine worktree 状态展示 | ❌ | ✅ |
| WorktreeExitDialog(退出提示) | ❌ | ✅ |
| 符号链接目录(node_modules 等) | ❌ | ✅ |
| sparse checkout | ❌ | ✅ |
| `--worktree` CLI 启动标志 | ❌ | ✅ |
| tmux 集成 | ❌ | ✅ |
| Arena 多模型 worktree 隔离 | ✅(qwen 独有) | ❌ |
| 脏状态覆盖(stash + copy) | ✅ | ✅ |
| Baseline commit 追踪 | ✅(qwen 独有) | ❌ |
| 功能 | qwen-code | claude-code | 阶段 |
| --------------------------------- | --------------- | ----------- | ------- |
| `EnterWorktree` 工具 | ✅(Phase A) | ✅ | — |
| `ExitWorktree` 工具 | ✅(Phase A) | ✅ | — |
| AgentTool `isolation: 'worktree'` | ✅(Phase B) | ✅ | — |
| 过期 worktree 自动清理 | ✅(Phase B) | ✅ | — |
| worktree 会话状态持久化与恢复 | ❌ | ✅ | Phase C |
| Post-creation setup(hooks 配置) | ❌ | ✅ | Phase C |
| StatusLine worktree 状态展示 | ❌ | ✅ | Phase C |
| WorktreeExitDialog(退出提示) | ❌ | ✅ | Phase C |
| `--worktree` CLI 启动标志 | ❌ | ✅ | Phase D |
| 符号链接目录(node_modules 等) | ❌ | ✅ | Phase D |
| sparse checkout | ❌ | ✅ | Future |
| tmux 集成 | ❌ | ✅ | Future |
| Arena 多模型 worktree 隔离 | ✅(qwen 独有) | ❌ | — |
| 脏状态覆盖(stash + copy) | ✅ | ✅ | — |
| Baseline commit 追踪 | ✅(qwen 独有) | ❌ | — |

## 设计原则

Expand Down Expand Up @@ -54,14 +54,14 @@ AgentTool 的 `isolation: 'worktree'` 只走通用路径,Arena 内部不经过

Arena 的 worktree 路径由 `agents.arena.worktreeBaseDir` 控制,默认 `~/.qwen/arena`(`ArenaManager.ts:125`),与通用路径完全独立,不做任何改动。

### 扩展配置(暂缓至 Phase C/D)
### 扩展配置

| 配置项 | 类型 | 用途 | 阶段 |
| ----------------------------- | ---------- | -------------------------------------------------------------- | ------- |
| `worktree.symlinkDirectories` | `string[]` | 符号链接指定目录(如 `node_modules`)到 worktree,避免磁盘浪费 | Phase C |
| `worktree.sparsePaths` | `string[]` | git sparse-checkout cone 模式,大型 monorepo 只写入指定路径 | Phase D |
| `worktree.symlinkDirectories` | `string[]` | 符号链接指定目录(如 `node_modules`)到 worktree,避免磁盘浪费 | Phase D |
| `worktree.sparsePaths` | `string[]` | git sparse-checkout cone 模式,大型 monorepo 只写入指定路径 | Future |

Phase A / B 不新增任何配置项。
Phase A / B / C 不新增任何配置项。

## 工具设计

Expand Down Expand Up @@ -187,27 +187,88 @@ _无需改动:_

---

### Phase C:体验优化(Post-creation setup + UI)
### Phase C:会话完整性(SessionService 持久化 + UI 安全网

**目标:** worktree 创建后自动初始化环境,状态在界面上可见
**目标:** worktree 状态在会话中断后可恢复,用户在界面上始终知道自己在哪个 worktree 里,退出会话时有安全提示

**要实现的功能:**

- Post-creation setup:配置 `core.hooksPath` 指向主仓库(qwen-code 无 `settings.local.json` 概念,不需要复制)
- StatusLine 展示当前 worktree 名称 / 分支
- WorktreeExitDialog:会话退出时(检测到 worktree 仍活跃)提示用户选择 keep 或 remove
- 新增 `worktree.symlinkDirectories` 配置项,实现目录符号链接
_SessionService worktree 状态持久化 + `--resume` 恢复:_

- `SessionService` 扩展 `WorktreeSession` 字段,记录 `{ slug, worktreePath, worktreeBranch, originalCwd, originalBranch }`
- `EnterWorktreeTool` 调用 `sessionService.setWorktreeSession()` 写入状态
- `ExitWorktreeTool` 调用 `sessionService.clearWorktreeSession()` 清除状态
- `--resume` 启动路径读取该字段,恢复 `targetDir` 并向模型注入上下文提示

_Post-creation setup:_

- 创建 worktree 后自动执行 `git config core.hooksPath <mainRepo>/.git/hooks`,确保 worktree 内的提交与主仓库 hooks 行为一致

_StatusLine worktree 展示:_

- `UIStateContext` 新增 `activeWorktree` 字段(从 session 状态读取),在会话进入 / 退出 worktree 时更新
- `StatusLineCommandInput` payload 新增 `worktree?: { slug: string; branch: string }` 字段,供用户 statusline 脚本使用
- `Footer` 在 `activeWorktree` 非空时内置展示一行 `⎇ <branch> (<slug>)`,无需用户配置 statusline 脚本即可获得基本可见性

_WorktreeExitDialog:_

- 新增 `WorktreeExitDialog.tsx` 组件,参考现有 Dialog 写法
- 修改退出键(Ctrl+C / Ctrl+D)处理逻辑:检测到 `activeWorktree` 非空时,拦截第二次确认,展示 Dialog 提示用户选择 keep 或 remove
- keep / remove 操作复用 `ExitWorktreeTool` 的现有路径

**影响文件:**

| 文件 | 变更类型 |
| ------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `packages/core/src/services/sessionService.ts` | 新增 `WorktreeSession` 字段及读写方法 |
| `packages/core/src/tools/enter-worktree.ts` | 调用 `sessionService.setWorktreeSession()` |
| `packages/core/src/tools/exit-worktree.ts` | 调用 `sessionService.clearWorktreeSession()` |
| `packages/core/src/services/gitWorktreeService.ts` | `createUserWorktree()` / `createAgentWorktree()` 后追加 `core.hooksPath` 配置 |
| `packages/cli/src/ui/contexts/UIStateContext.tsx` | 新增 `activeWorktree` 字段及 set/clear action |
| `packages/cli/src/ui/hooks/useStatusLine.ts` | `StatusLineCommandInput` 新增 `worktree` 字段 |
| `packages/cli/src/ui/components/Footer.tsx` | 内置 worktree 行展示 |
| `packages/cli/src/ui/components/WorktreeExitDialog.tsx` | 新建 |
| `packages/cli/src/ui/components/DialogManager.tsx` | 注册 `WorktreeExitDialog` |
| `packages/cli/src/ui/components/ExitWarning.tsx` 或退出键处理 | 检测 `activeWorktree` 并拦截退出 |

---

### Phase D:高级功能
### Phase D:启动时配置(`--worktree` CLI 标志 + 目录符号链接)

**目标:** 对齐 claude-code 的完整特性集
**目标:** 支持在启动时直接进入 worktree,并通过目录符号链接减少大型项目的磁盘开销

**要实现的功能:**

- `--worktree [name]` CLI 启动标志:启动时直接创建 worktree,整个会话在隔离环境中运行
- sparse checkout 支持:新增 `worktree.sparsePaths` 配置项
- `.worktreeinclude` 文件:支持将 gitignore 的文件复制到 worktree
- tmux 集成:`--worktree --tmux` 在 tmux 会话中启动
- PR 引用解析:`--worktree=#123` 自动 fetch 并基于 PR 创建 worktree
_`--worktree [name]` CLI 启动标志:_

- `packages/cli/src/args.ts` 新增 `--worktree [name]` 参数
- 启动流程在进入主循环前调用 `createUserWorktree()`,将 `targetDir` 设为 worktree 路径,并写入 SessionService 状态
- 整个会话从启动即在 worktree 环境中运行,退出时触发 WorktreeExitDialog

_`worktree.symlinkDirectories` 配置项:_

- settings schema 新增 `worktree.symlinkDirectories: string[]`
- `createUserWorktree()` 后遍历配置,调用 `fs.symlink()` 将主仓库目录链接进 worktree
- 跳过目标不存在的项;目标已存在时跳过(不覆盖)

**影响文件:**

| 文件 | 变更类型 |
| -------------------------------------------------- | ------------------------------------------- |
| `packages/cli/src/args.ts` | 新增 `--worktree [name]` 参数 |
| `packages/cli/src/main.ts`(或启动入口) | 解析 `--worktree` 并在主循环前创建 worktree |
| `packages/core/src/services/gitWorktreeService.ts` | `createUserWorktree()` 后追加 symlink 逻辑 |
| `packages/core/src/config/`(settings schema) | 新增 `worktree.symlinkDirectories` 字段 |

---

### Future:高级功能(按需实现)

以下功能面向更特定的使用场景,当前阶段不纳入排期,待用户需求明确后再评估实现。

| 功能 | 说明 |
| ----------------------- | ------------------------------------------------------------------------------------------- |
| sparse checkout | `worktree.sparsePaths` 配置项,大型 monorepo 只 checkout 指定路径,缩短创建时间和磁盘占用 |
| `.worktreeinclude` 文件 | 将 gitignore 的文件(`.env`、`secrets.json` 等)自动复制进 worktree |
| tmux 集成 | `--worktree --tmux` 在新 tmux 窗口启动 worktree 会话 |
| PR 引用解析 | `--worktree=#123` 自动 fetch PR 分支并基于它创建 worktree(依赖 Phase D `--worktree` 标志) |
Loading
Loading