---
title: Worktrees
description: Give a chat its own branch and folder, then merge its work back into your project.
canonical_url: https://lookout.itoowasinarcadia.com/docs/worktrees
type: guide
section: Use Lookout
last_updated: 2026-10-22
related:
  - https://lookout.itoowasinarcadia.com/docs/checkpoints
  - https://lookout.itoowasinarcadia.com/docs/subagents
  - https://lookout.itoowasinarcadia.com/docs/machines
---

> Every page of these docs: https://lookout.itoowasinarcadia.com/llms.txt

# Worktrees

Give a chat its own branch and folder, then merge its work back into your project.

A worktree gives a chat its own git branch in its own folder, apart from your checkout and other chats' work. Use
one when several chats work on the same project at once, or when you want to read the work before it reaches your
branch.

## Start a chat in a worktree

1. Press `⌘N` and pick a project that is a git repository with at least one commit. **Worktree** shows only for one.
2. Turn on **Worktree** beside the project.
3. Send your message.

Lookout creates a branch such as `lookout/chat-720db589` (the start of the chat's ID) and checks it out in a folder
under `~/.lookout/worktrees/`. The chat's tools, its
[checkpoints](https://lookout.itoowasinarcadia.com/docs/checkpoints#review) and **Changes** all work in that folder.

The branch starts from the fetched tip of the branch your checkout tracks (the remote's default branch when the
checkout is detached or tracks nothing) when your checkout has no commits of its own past it, so a chat on a stale
checkout still starts current. A checkout with its own commits past that tip keeps them: the branch starts from its
HEAD, as does a checkout whose remote cannot be fetched. Pass `base` to start from a branch, tag or HEAD of your
choosing instead.

A brand new folder holds only what is committed: files such as `.env` and installed dependencies are not there until a
[setup command](https://lookout.itoowasinarcadia.com/docs/worktrees#setup) brings them. When an earlier chat's worktree of the project was removed, Lookout
reuses its folder instead: it switches that folder to the new branch, so only the files that differ are written, and
installed dependencies and build output git ignores there are still in place. Every other file the earlier chat left,
such as its `.env`, is deleted first, and so are environment, key and Terraform state files inside build output. The setup command runs either way. **Worktree** can't be turned on for Scratch, which isn't a git
repository.

A worktree or clone you made yourself is not a new project either: open its folder and the chat starts in the
project of its repository, working in that folder ([Open a folder](https://lookout.itoowasinarcadia.com/docs/chats#open-folder)). Lookout neither merges
nor removes it; that checkout stays yours.

## Two chats in one folder

When two chats, or a chat and a subagent, change files in the same checkout within ten minutes of each other, each
is told once: which chat it is, the files it changed, and how to message it, so neither undoes the other's work in
progress. The chat shows it as one row, **Another chat changed files in this checkout**, which opens on those files
and **Open chat**. Nothing is stopped or locked. A chat an agent or an orchestrator starts in a project folder another chat
is changing gets its own worktree, and the agent's answer says why, unless it asked for the folder itself (worktree
off, or a folder named). A chat you start yourself stays where you start it.

## Merge the work back

Only commits merge. Ask the agent to commit its work, or commit it yourself in the worktree's folder.

1. Press `⇧⌘R` to open the side pane, and choose **Changes**.
2. Choose **Merge branch**, then **Merge**.

Lookout fast-forwards your project's current branch when it can, and otherwise makes a merge commit. A toast reads
**Merged into** and your branch's name, or **Already up to date**.

The merge stops, and your checkout stays as it was, when:

- the worktree has uncommitted changes
- your project checkout has uncommitted changes, or isn't on a branch
- the merge conflicts. Ask the agent to merge your branch into its worktree and resolve the conflict, then choose
  **Merge branch** again.

An agent can do the same merge for a chat when asked: it reads the chat's changes, merges,
and its chat shows a **Merged** row with your branch's name. The worktree and its branch stay either way; removing
them is yours.

## Run a setup command in each new worktree

1. In the sidebar, open the project's menu and choose **Project settings**.
2. Under **Worktree**, choose **Edit** beside **Worktree commands**, type a **Setup** command, such as `bun install`, and
   choose **Save**. Add a **Teardown** command if a worktree needs cleaning up when it goes.

**Setup** runs in each new worktree before the chat starts, and its output shows in the chat as **Worktree setup**. If
it fails, Lookout doesn't send your message: it stays in the box, and the error says why. A chat an agent or another
app starts answers at once, before its worktree is made: its first turn waits for the worktree and its setup, and if
either fails the chat ends failed with the reason, its message kept. **Teardown** runs when Lookout removes the worktree.

Each command runs with `sh -c` in the worktree, counts as failed after 10 minutes, and gets these variables:

| Variable | Value |
|---|---|
| `LOOKOUT_BASE_REF` | the branch, or `HEAD`, the worktree started from |
| `LOOKOUT_CHAT_ID` | the chat's ID |
| `LOOKOUT_MACHINE` | the name of the machine the chat runs on |
| `LOOKOUT_WORKTREE` | the worktree's folder |

Lookout's own settings and a model provider's sign-in stay out of the command's environment.

A setup that copies your project's `.env` into the worktree and installs dependencies:

```sh
cp "$(git rev-parse --git-common-dir)/../.env" .env && bun install
```

Install dependencies in the worktree itself rather than linking your checkout's `node_modules` into it: through such a
link, the project's own workspace packages load from your checkout, not from the worktree's branch.

### A project without a setup command

Without a setup command, a new worktree has no dependencies installed. When your project's folder holds a lockfile,
Lookout says so and names the command that installs from it:

- The new chat shows **Worktree setup not run**, and its details name the command and where to set it. A workflow
  agent's worktree says the same in its steps.
- Under **Worktree**, **Project settings** says new worktrees start without dependencies, with the command. Choose
  **Use as setup** to save it as the setup command, approved on this machine, or type your own under **Edit**.

Lookout runs nothing until you set it.

| Lockfile | Command |
|---|---|
| `bun.lock`, `bun.lockb` | `bun install --frozen-lockfile` |
| `pnpm-lock.yaml` | `pnpm install --frozen-lockfile` |
| `package-lock.json` | `npm ci` |
| `yarn.lock` | `yarn install --immutable`, or `yarn install --frozen-lockfile` when the folder has no `.yarnrc.yml` (Yarn 1) |
| `uv.lock` | `uv sync --frozen`, after the JavaScript command when there is one |

## The worktree file

Lookout saves these commands in `.lookout/worktree.json` in your project. Commit it, and a clone on another machine has
the same commands, which run there once you [approve them](https://lookout.itoowasinarcadia.com/docs/worktrees#approve). The file can also set what the
shell gets in every chat of the project, worktree or not:

```json
{
  "setup": "bun install",
  "teardown": "docker compose down",
  "path": ["node_modules/.bin"],
  "env": { "NODE_ENV": "development" },
  "requires": ["bun", "docker"]
}
```

| Key | What it does |
|---|---|
| `setup`, `teardown` | the commands above |
| `path` | folders put in front of `PATH` for the project's shell commands and hooks: `~/` is your home folder, a relative folder sits in the chat's folder |
| `env` | variables for the project's shell commands and hooks. The file is committed, so never put a secret here. It can't set `PATH` or a `LOOKOUT_` variable |
| `requires` | commands the project needs: `lookout doctor` names the missing ones |

While the file has an error, a new worktree chat doesn't start, and the error says what to fix.

## Approve a worktree file

Lookout runs a worktree file's `setup`, `teardown`, `path` and `env` on a machine only once you approve exactly what the
file holds there. What you save in **Project settings** is approved as you save it. A file that came with a clone, or
changed since you approved it (a pull, an edit outside Lookout), waits:

- Under **Worktree**, **Project settings** says the repo's worktree commands do not run here until you approve them, or
  that they changed since you approved them, with each command and what the file adds to `PATH` and the environment.
  Read them, then choose **Approve**.
- Until then, a new worktree chat starts without its setup and shows **Worktree setup not run**, teardown is skipped
  the same way, and the project's shells get none of its `path` or `env`. `lookout doctor` names the project.

From another app, `lookout_projects` shows the file's `trust` and `fingerprint` under `worktree`, and
`lookout_configure_project` with `approveWorktree` set to that fingerprint approves it. Only you, or an app acting
for you, approves or writes the file this way: a chat's agent, from its shell, can't.

## Remove a worktree

Archiving a chat removes its worktree's folder when nothing in it is uncommitted: no uncommitted changes or untracked
files. Its branch goes too when its commits are merged into your project's branch or its remote's default branch, or
pushed. A commit landed by rebase or cherry-pick counts as merged when its change is there (`git cherry` shows no
`+`), and a branch counts as pushed when any ref the remote holds reaches it, under any name. A branch whose commits
are not merged or pushed stays with every commit, the answer names them, and the next message to the chat makes the
folder again on it ([When a chat's worktree is gone](https://lookout.itoowasinarcadia.com/docs/worktrees#gone)). Dependency and build output such as
`node_modules` or `__pycache__` doesn't count as untracked. When the folder holds uncommitted work the worktree stays
and the archive's answer says why. Lookout checks such a worktree again every hour by the same rule, and removes it
once it is safe; archiving the chat again tries at once. The archive prompt's **Keep it** leaves the worktree and its
branch where they are, and the hourly check leaves it too. So does a worktree archived before Lookout recorded why an
archive kept it: it stays until you restore its chat from **Settings › Archive** and archive it again. Deleting a chat asks **Remove the worktree?** and removes it only
when its commits are merged or pushed as well, since no chat is left to bring it back. An agent archiving a chat it
owns follows the archive's rule too (an agent can't keep one the rule would remove). An agent's archive answers at once
and the check runs after it: a worktree that goes shows in the archived chat as **Worktree teardown**, and the agent
hears in one notice each chat whose worktree stayed and why, and each whose worktree went: its folder, and its branch
at the commit removed and where that commit was merged or pushed, or the branch that stays.

The check judges the commit the branch points to when it starts. If the worktree changes while it runs (a new commit,
a rebase, an edit) or a rebase, merge or cherry-pick is under way in it, the worktree and its branch stay and the
answer says so; a branch that moved after the check stays too. From its last look at the worktree to the removal,
Lookout holds the worktree's git index lock, so a commit or rebase started in it right then fails with git's own
`index.lock` message and changes nothing, and what Lookout removes is the commit it judged.

A worktree folder that is no longer a checkout (its `.git` went with the worktree git removed) counts as gone:
archiving marks it removed and deletes the folder when it holds only Lookout's own `.lookout` state. A worktree
whose branch was deleted is judged by its checkout's own commit instead.

Removing a project also removes its chats' worktrees that are safe to remove, and leaves the others on disk. Lookout
removes a chat's worktree at no other time than these and the hourly check of archived chats.

A removed worktree's [checkpoints](https://lookout.itoowasinarcadia.com/docs/checkpoints) go with it, so its chat's turns can no longer be reviewed or
restored. The chat itself stays readable. Its files are gone too: the chat's **Files** tab and
[`lookout files`](https://lookout.itoowasinarcadia.com/docs/cli#files) say the worktree was removed rather than show or change the project's checkout,
until a message makes it again ([When a chat's worktree is gone](https://lookout.itoowasinarcadia.com/docs/worktrees#gone)).

A removed worktree's folder waits in `~/.lookout/worktrees/<project>/.pool/` for the project's next worktree chat,
which takes it over ([Start a chat in a worktree](https://lookout.itoowasinarcadia.com/docs/worktrees#start)). Lookout keeps up to six such folders per
project, deletes one after three days, never reuses one while a process is still running in it or while it has changes,
and deletes them all when you remove the project. Moving a folder into the pool or out of it rewrites that worktree's
own git links only, never another worktree's. Set `LOOKOUT_WORKTREE_POOL` to another number of folders, or to `0` to
delete every removed worktree's folder at once.

On a Mac the pool also keeps `.seed`, a checkout no chat works in. When no removed worktree is free, a new one is a
copy-on-write clone of it: its files take no disk space of their own until a chat changes them. Set
`LOOKOUT_WORKTREE_SEED` to `0` to have git write out every file instead.

## When a chat's worktree is gone

A chat outlives its worktree, whether an archive removed it or something outside Lookout did (a landing script that
deletes the folder and branch it ran in, say). The next message to it (yours, an agent's Steer, a Delegate into its
lane, or a Retry) makes the worktree again at the same folder before its turn runs, with the project's
[setup command](https://lookout.itoowasinarcadia.com/docs/worktrees#setup): on its branch when the branch still exists in the repository or on its remote,
else on a branch from the base a new chat would get. The answer to the message says where and on what, and the
chat's agent is told the old folder was removed, whether the branch's last commit is in the base, and that files
that were never committed went with it. Lookout never runs the chat in the project's own checkout instead.

Something that wakes a chat without a message (a background command ending, a watch, a subagent finishing) does the
same: the worktree is made again and the woken turn runs there, so a chat whose landing script removed its folder
still reports how the landing went. A background command that ended while the folder was gone has its output file
written back where the chat's notice says it is. Until its next turn, the chat's lane reads **worktree removed** in
[Sessions](https://lookout.itoowasinarcadia.com/docs/agents). Continuing a subagent of such a chat is refused until a message has made the worktree again.

## Related

- [Checkpoints and changes](https://lookout.itoowasinarcadia.com/docs/checkpoints.md): Lookout records the chat's folder before every turn, so you can review what the agent changed and put files back.
- [Subagents](https://lookout.itoowasinarcadia.com/docs/subagents.md): The agent can hand parts of a task to subagents that report back, waiting for them or working on while they run.
- [Machines](https://lookout.itoowasinarcadia.com/docs/machines.md): Run chats on your other Macs and on your servers, from the app or the terminal.
