kunchenguid/treehouse
GitHub: kunchenguid/treehouse
treehouse 通过维护可复用的 Git worktree 池,为开发者和 AI agent 提供即时隔离的工作环境,无需手动管理或重复克隆。
Stars: 264 | Forks: 23
treehouse
无需手动管理 worktree,即可轻松管理 worktree。
你还在一次只处理一个任务吗?你还在手动在同一个 repo 的几个克隆之间来回切换吗?
或者……你是不是每次 agent 会话都在新建一个 worktree,每次都丢失所有已安装的依赖和构建缓存,然后纳闷为什么你的 agent 这么慢?
Treehouse 可以帮你管理一个可复用、相互隔离的 worktree 池,这样你的每个 agent 都能立即获得自己的环境——无需克隆、无冲突、也没有协调开销。
- **即时隔离** — `treehouse` 能让你毫无负担地进入一个干净的 worktree。
- **可复用 worktree** — 当你完成任务后,worktree 会连同完整的依赖和构建缓存一起保留在池中,随时为下一个 agent 准备就绪。
- **无冲突** — 自动检测正在使用中的 worktree,确保你的 agent 之间绝不会互相干扰。
## 快速开始
```
$ cd myproject # start in your repo as usual
$ treehouse # get a worktree and drop into a subshell
🌳 Entered worktree at ~/.treehouse/myproject-a1b2c3/1/myproject. Type 'exit' to return.
# 你现在处于一个隔离的 worktree 中。
# 运行你的 AI agent,进行更改,做任何你需要做的事。
$ exit # exit the subshell when you're done
🌳 Terminated lingering processes: opencode (pid 12345)
🌳 Worktree returned to pool.
```
## 安装
**macOS / Linux**
```
curl -fsSL https://kunchenguid.github.io/treehouse/install.sh | sh
```
**Windows (PowerShell)**
```
irm https://kunchenguid.github.io/treehouse/install.ps1 | iex
```
**Nix**
```
nix run github:kunchenguid/treehouse
```
或者添加到你的 flake inputs 中:
```
treehouse = {
url = "github:kunchenguid/treehouse";
inputs.nixpkgs.follows = "nixpkgs";
};
```
**Go**
```
go install github.com/kunchenguid/treehouse@latest
```
**从源码构建**
```
git clone https://github.com/kunchenguid/treehouse.git
cd treehouse
make install
```
## 工作原理
Treehouse 为每个 repo 管理**一个 git worktree 池**,存储在配置的 treehouse 根目录下。
默认的 treehouse 根目录是 `~/.treehouse/`。
```
treehouse
│
▼
Find repo root
│
▼
git fetch origin
│
▼
┌───────────────────────────────────────┐
│ Scan pool for available worktree │
│ (not leased, not in-use, not dirty) │
└──────────┬────────────────────────────┘
│
┌────┴────┐
│ Found? │
└────┬────┘
yes/ \no
/ \
▼ ▼
Reset to Create new worktree
latest (detached HEAD at
default latest default
branch branch)
& add to pool
\ /
\ /
▼
Spawn subshell in worktree
(agent works here)
│
▼
exit subshell
│
▼
Terminate lingering worktree
processes, reset worktree,
& return to pool
(ready for next agent)
```
- **Detached HEAD** — worktree 使用 detached HEAD 模式,并重置到本地或远程默认分支中最靠前的那个位置,从而完全避免分支名冲突。
- **无 daemon** — 所有操作都是内联 CLI 命令。没有后台进程,也没有会损坏的状态。
- **使用中检测** — treehouse 会扫描运行中的进程以及短暂的持有者预留记录,以确定哪些 worktree 正在使用中。预留状态仅在执行 `get`、`destroy` 和 `prune` 等生命周期任务时进行持久化。
- **持久租约** — `treehouse get --lease` 会将一个 worktree 预留为持久归属地,且不需要在其中保持进程运行。该租约会记录在 treehouse 自身的状态中,因此该 worktree 绝不会被随后的 `get` 分配出去,也绝不会被 `prune` 移除,直到你通过 `treehouse return` 释放它。与基于进程的使用中检测不同,即使 worktree 内部没有任何进程在运行,租约依然有效。
- **脏状态检测** — treehouse 会将已跟踪的修改和未跟踪的文件视为脏状态,即使 repo 配置在正常的 `git status` 输出中隐藏了未跟踪的文件。
- **安全清理** — 默认情况下,`treehouse prune` 仅移除那些 HEAD 已经合并到默认分支且工作树干净的空闲受管 worktree。
`treehouse prune --all` 会对用户级 treehouse 根目录下的每个受管池应用相同的安全检查。
默认情况下会报告缺失底层 repo 的孤立项;`--prune-orphans` 会将它们列为未经验证的清理候选对象,并且在删除前必须要求 `--yes`。
除非你传入 `--yes`,否则它将进行试运行。
## CLI 参考
| 命令 | 描述 |
| -------------------------- | ---------------------------------------------------- |
| `treehouse` | 获取一个 worktree 并打开子 shell(`get` 的别名) |
| `treehouse get` | 从池中获取一个 worktree |
| `treehouse get --lease` | 持久租用一个 worktree 而不开启子 shell;打印其路径 |
| `treehouse status` | 显示池状态(高亮显示已租用和当前的 worktree) |
| `treehouse return [path]` | 释放任何租约,终止残留的 worktree 进程,并将其返回到池中 |
| `treehouse prune` | 试运行移除当前 repo 池中陈旧的空闲 worktree |
| `treehouse prune --all` | 试运行移除每个受管池中陈旧的空闲 worktree |
| `treehouse destroy [path]` | 从池中移除一个 worktree |
| `treehouse init` | 创建默认的 `treehouse.toml` 配置文件 |
| `treehouse update` | 将 treehouse 更新到最新版本 |
### 标志
| 命令 | 标志 | 描述 |
| --------- | --------- | --------------------------------- |
| `get` | `--lease` | 持久租用 worktree 而不打开子 shell;仅将其路径打印到 stdout |
| `get` | `--lease-holder` | 作为租约持有者记录的可选标签(默认为 `$TREEHOUSE_LEASE_HOLDER`) |
| `return` | `--force` | 无需提示直接清理、重置并返回 |
| `prune` | `--yes` | 删除列出的清理候选对象,而不是进行试运行 |
| `prune` | `--all` | 扫描用户级 treehouse 根目录下的每个受管池 |
| `prune` | `--global` | `--all` 的别名 |
| `prune` | `--prune-orphans` | 将缺失底层 repo 的孤立项包含在清理候选对象中 |
| `prune` | `--verbose`, `-v` | 显示详细的跳过诊断信息 |
| `destroy` | `--force` | 即使正在使用或已租用,也强制销毁 |
| `destroy` | `--all` | 销毁池中的所有 worktree |
### 租用 worktree(无子 shell)
`treehouse get` 通常会打开一个交互式子 shell,其生命周期即为持有时间:当 shell 退出时,worktree 就会返回到池中。
对于需要一个作为永久归属地且内部没有长期运行进程的调用方来说,这很不方便。
`treehouse get --lease` 提供了一种非交互式、持久化的替代方案:
```
path=$(treehouse get --lease)
# $path 是租用的 worktree 的绝对路径;所有 banner 都输出到了 stderr。
```
它的获取 worktree 的方式与 `get` 完全相同,但它不会打开子 shell,而是将 worktree 在 treehouse 的持久化状态中标记为**已租用**,并且仅将该 worktree 的绝对路径打印到 stdout(所有面向用户的消息都会进入 stderr,从而保证命令替换输出的纯净)。
只要租约没有被明确释放,无论其中是否有进程在运行,已租用的 worktree 就绝不会随后被 `get` 分配出去,也绝不会被 `prune` 移除。
此外,它还需要使用 `--force` 才能被 `destroy`。
传入 `--lease-holder
标签:AI工程, EVTX分析, Git, Python安全, SOC Prime, 安全可观测性, 工作树, 开发工具, 日志审计, 版本控制, 网络安全研究, 网络调试, 自动化