> ## Documentation Index
> Fetch the complete documentation index at: https://aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# aspect worktree list

> Show every slot in this repository's worktree pool with the branch holding it and whether its Bazel server and output base are still there.

<Warning>
  New and experimental. File bugs and suggestions to [aspect-build/aspect-cli/issues](https://github.com/aspect-build/aspect-cli/issues).
</Warning>

Every slot in this repository's pool, what holds it, and how warm it is.

```shell theme={null}
aspect worktree list
```

```
~/.aspect/worktrees/github.com/acme/web/aspect-worktree-<slot>
4 slots: 2 taken, 2 free

taken
  slot          state   warmth  age  leases  branch      agent        session                               notes
  18a11aa20e8d  in use  warm    12d       7  fix/login   claude-code  b22fe20c-1111-4000-8000-00000000b22f  session resumable
  b2697ff9b4e3  in use  cold    12d       4  spike/perf  claude-code  7f1e9a02-dead-4000-8000-00000000dead  session gone

free
  slot          warmth  last used  age  leases  last branch     notes
  f066bb8445cc  warm          <1m  12d       9  fix/logout
  0b8751be74ee  cold          41d  41d       1  old-experiment  base reclaimed

  the process that took fix/login is no longer running; resume its session:
    cd ~/src/web && claude --resume b22fe20c-1111-4000-8000-00000000b22f

  For more details run: aspect worktree list --verbose
  For usage instructions run: aspect worktree list --help
```

The first line is the pool's own directory, written as a template whose variable is the slot column — so any row's path is the heading with that row's slot substituted in. Reading the tables is then the same exercise as reading `git worktree list`, which this deliberately resembles. The line under it counts the slots and how many are taken and free, which answers whether you can get one before you read a row. Run from a clone, a free slot of another clone of the repository, or of one that is gone, is counted apart as `free but not this clone's`, since it is not yours to take; outside any clone there is no "yours", and every free slot counts as free.

Slots come in two tables, because what is worth knowing about a slot somebody holds and one you could take are different things. A taken slot is held, and the question is by whom; a free slot is available, and the question is how warm it is and how long it has sat. Each heading sits at the left margin, outside its indented table, so it stands apart even without color; on a terminal it is highlighted and the column headers are dimmed. Color follows stderr, where the report is written, and is off under `NO_COLOR`.

Within each, columns run from the slot itself outward, which keeps the fixed-width ones on the left so the numbers read as a block and the ones that vary in width fall at the end. Rows are ordered by what is happening: in use first, warmest down to cold, then the slots you could take, most recently used first.

A column no slot fills is left out. `agent` is the exception: it is shown even when nothing is running, because a column that disappears would never tell you the pool records which session holds a slot. `notes` carries only exceptions, so it does disappear.

What the columns and their values mean lives in `aspect worktree list --help`, which the footer points at, rather than under every listing.

## Getting back to a session

A session id is printed whole, in its own column, so it can be handed straight to the harness — an abbreviated UUID identifies a session only to someone who already knows which one it is, and `--resume` will not take it.

The command that returns to a session is in [`--verbose`](#every-lease-a-slot-has-held), in a section per session. That is also how the sessions which held a slot *earlier* are reachable, which a table of present holders could not express.

One case is surfaced without `--verbose`, because it is the one you act on: a slot whose holder has stopped and whose session the harness still has. A closed terminal is almost always what that means, and the work in it is waiting to be picked up rather than finished with, so the listing offers the way back in rather than the way to take the slot away.

```
  the process that took fix/login is no longer running; resume its session:
    cd ~/src/web && claude --resume b22fe20c-1111-4000-8000-00000000b22f
```

The `cd` is there because `claude --resume <id>` resolves within the current directory's project, so the bare command would only work where you already happened to be standing. It is harmless for the harnesses that do not need it, which spares you having to know which those are.

Where a harness marks its presence without naming a session — Gemini CLI and Cursor both do — the `session` cell is left blank, and `--verbose` prints `-` for it. That is a statement rather than a gap: the answer is unavailable, not empty. No command is offered for a session the harness's own store was read and found not to have, since one that resolves nothing is worse than none at all.

Slots held by a person report no agent; there is no session to name.

## One repository at a time

A pool belongs to one repository — slots are keyed by the repository your clone points at — so this lists the slots of the clone you are standing in and never another repository's, the same way `git worktree list` reports one repository's worktrees.

`aspect worktree list --all` is the other way to widen the view, and the one
that stays within the pool: every pool on this machine, each under its own
heading. It works outside a repository too, since "where are my agents" gets
asked from anywhere.

```shell theme={null}
aspect worktree list --all
```

```
~/.aspect/worktrees/github.com/acme/api/aspect-worktree-<slot>
1 slot: 0 taken, 1 free

free
  slot          warmth  last used  age  leases  last branch
  aaaaaaaaaaaa  cold          12d  12d       4  fix/checkout

~/.aspect/worktrees/github.com/acme/web/aspect-worktree-<slot>
4 slots: 2 taken, 2 free

taken
  slot          state   warmth  age  leases  branch      agent        session                               notes
  18a11aa20e8d  in use  warm    12d       7  fix/login   claude-code  b22fe20c-1111-4000-8000-00000000b22f  session resumable
  b2697ff9b4e3  in use  cold    12d       4  spike/perf  claude-code  7f1e9a02-dead-4000-8000-00000000dead  session gone

free
  slot          warmth  last used  age  leases  last branch     notes
  f066bb8445cc  warm          <1m  12d       9  fix/logout
  0b8751be74ee  cold          41d  41d       1  old-experiment  base reclaimed

  the process that took fix/login is no longer running; resume its session:
    cd ~/src/web && claude --resume b22fe20c-1111-4000-8000-00000000b22f

  a pool this clone does not own is read from its records: a slot still on disk reads as its records left it, so a lease there may have ended already

  For more details run: aspect worktree list --all --verbose
  For usage instructions run: aspect worktree list --help
```

One thing it cannot do is check another pool against git. `git worktree list`
answers for one object store, so only the pool belonging to the clone you run
from is reconciled; the others report the branch their own records hold, and the
listing says so beneath the tables. What *is* current for every pool is anything
read from disk — whether a slot's directory is still there, and how warm its
output base is — because none of that goes through git.

`aspect output-bases` is the machine-wide view of a different thing. It walks the Bazel output user root rather than any one pool, so every pool's slots appear in it, each attributed to its repository and showing what it holds or last held, beside every other output base on the machine. A pool whose builds went to a different output user root holds state neither command can see; `--all` says so when this workspace is the one that moved its root.

A pool's slots get their own heading there, each row carrying the repository and the lease:

```
  worktree pool slots (2):
    last used  output base                       repository           branch                  workspace
       0m ago  e9a33320d289064646ca04497ef4fcec  github.com/acme/web  free · last fix/logout  ~/.aspect/worktrees/github.com/acme/web/aspect-worktree-f066bb8445cc
       0m ago  c84887dbc6585633e5acf6a25179882c  github.com/acme/web  leased · fix/login      ~/.aspect/worktrees/github.com/acme/web/aspect-worktree-18a11aa20e8d
```

A leased slot reads `leased · <branch>`, followed by its holder when an agent took it. A slot whose clone is no longer at its recorded path adds `owner gone`: `leased · <branch> · owner gone · <holder>`, or `free · last <branch> · owner gone`. `aspect output-bases` reads no git, so it cannot tell a moved clone from a deleted one; this command's `clone moved` and `stranded` notes do.

<Note>
  `aspect output-bases` only knows a slot that has been built in, since an output base is the thing it enumerates. A slot nobody has built in yet has nothing for it to find, and only this command will show it.
</Note>

## Every lease a slot has held

`--verbose` adds a block per slot — its paths, any server holding it, and the slot's remembered holders, newest first — each block headed by the slot, which is what the tables above are keyed on. Then a section per agent session those holders name:

```
  18a11aa20e8d
    branch:      fix/login
    path:        ~/.aspect/worktrees/github.com/acme/web/aspect-worktree-18a11aa20e8d
    output base: ~/Library/Caches/bazel/_bazel_you/c84887dbc6585633e5acf6a25179882c
    leases:      7, slot created 12d ago, the last 2 shown
      branch      held  status            agent        session                               project
      fix/login   1h    still holding it  claude-code  b22fe20c-1111-4000-8000-00000000b22f  ~/src/web
      fix/logout  1h    ended 1h 6m ago   claude-code  b22fe20c-1111-4000-8000-00000000b22f  ~/src/web

  b2697ff9b4e3
    branch:      spike/perf
    path:        ~/.aspect/worktrees/github.com/acme/web/aspect-worktree-b2697ff9b4e3
    output base: ~/Library/Caches/bazel/_bazel_you/540b29d4073e3c9c6702541d05811ef8  (not created yet)
    leases:      4, slot created 12d ago, the last 1 shown
      branch      held  status            agent        session
      spike/perf  30m   still holding it  claude-code  7f1e9a02-dead-4000-8000-00000000dead

  f066bb8445cc
    last branch: fix/logout
    path:        ~/.aspect/worktrees/github.com/acme/web/aspect-worktree-f066bb8445cc
    output base: ~/Library/Caches/bazel/_bazel_you/e9a33320d289064646ca04497ef4fcec
    leases:      9, slot created 12d ago, the last 1 shown
      branch      held  status           agent        session                               project
      fix/logout  1h    ended 1h 6m ago  claude-code  b22fe20c-1111-4000-8000-00000000b22f  ~/src/web

  0b8751be74ee
    last branch: old-experiment
    path:        ~/.aspect/worktrees/github.com/acme/web/aspect-worktree-0b8751be74ee
    output base: ~/Library/Caches/bazel/_bazel_you/a7ecd397001f7bdaedd3587c95396709  (reclaimed)
    leases:      1, slot created 41d ago
      branch          held  status         agent  session
      old-experiment  2d    ended 41d ago  -      -

  session 7f1e9a02-dead-4000-8000-00000000dead
    agent:   claude-code
    project: -
    resume:  -
    leases:
      repo                 slot          branch      held  status
      github.com/acme/web  b2697ff9b4e3  spike/perf  30m   still holding it

  session b22fe20c-1111-4000-8000-00000000b22f
    agent:   claude-code
    project: ~/src/web
    resume:  claude --resume b22fe20c-1111-4000-8000-00000000b22f
    leases:
      repo                 slot          branch      held  status
      github.com/acme/web  18a11aa20e8d  fix/login   1h    still holding it
      github.com/acme/web  18a11aa20e8d  fix/logout  1h    ended 1h 6m ago
```

A slot remembers its last ten holders, and says how many of them it is showing. Each row says which branch was in it, how long they kept it and whether they still do, which agent session had it, and the project the harness filed the session under. `project` drops out for a slot none of whose holders could be placed.

The sessions come last: each one's harness, its project, the command that returns to it, and every lease it took, active first. Most leases come from subagents, which report their parent's session, so the way back into a conversation is said once there rather than on every row it touched. A holder that let go of a slot is as resumable as the one holding it now, which is why its session is listed all the same. Where the harness's store was read and does not have the session — `7f1e9a02` above — `resume` reads `-`, because a command that resolves nothing is worse than none.

The output base line says why a base is absent, since the two reasons send you different places. `(not created yet)` is a slot nobody has built in, and the path is where a base will go. `(reclaimed)` is a base that was there once and has been collected — by `aspect gc` or a `bazel clean --expunge` — which the slot survives and rebuilds on its next build.

The holder is recorded as the harness, the session and the directory it ran in. A finished session's pid says nothing and is not kept; the directory is a different kind of fact — harnesses file a session under the project it was opened in, so it is what lets a past holder's session be placed.

Two things do not add to `leases`: taking a slot over with `--take-over`, which hands an existing checkout to another session without rebuilding anything, and a worktree a person created by hand, which the pool does not manage. A take-over does appear in the history, because the slot changed hands.

### Where the output base is

Bazel derives a workspace's output base as `md5(workspace_path)` under the output user root, so a slot's fixed path is enough to find its state without asking a server — and `bazel`, `aspect`, an IDE or a script in that directory all reach the same base. The root is resolved as Bazel resolves it: `startup --output_user_root=` from the rc chain where a repo or your `~/.bazelrc` sets one, else the platform default. A listing reads warmth under that root, and names it under `--all` when it is not the default.

## Reading it

**`in use` / `free`** says whether a worktree is currently checked out in the slot. A free slot keeps its Bazel state and is what the next `aspect worktree add` reuses.

**`setup`** is the third state: an `aspect worktree add` has claimed this slot and is checking the branch out into it. It lasts as long as that checkout, and the slot is not available to anything else meanwhile — including [`prune`](/docs/cli/worktrees/prune). The branch column shows the branch being checked out; where it shows something else, the notes add `claimed for <branch>`. If the `add` that claimed it is no longer running, the notes say `add abandoned`, a line under the table says so, and the next `add` from the clone that owns it reclaims the slot.

**`age` and `leases`** are the pool's return on the disk it is holding. `leases` counts the worktrees a slot has held, which is how many cold starts it saved; a slot reading `1` has never been reused, and a pool where every slot reads `1` is one that grew to your peak concurrency and has not turned over yet.

**`last used`** is how long since the slot was last used: Bazel's last activity in its output base — the same evidence `aspect gc` measures a base by — or a worktree taken or given back there, whichever is newer. The free table is ordered by it, so the slot you just released is at the top, and it is the number [`aspect worktree prune`](/docs/cli/worktrees/prune) acts on, so it is shown rather than left to be guessed at. A slot that has held nothing is aged from its creation. Both it and `age` are shown in the coarsest unit that still distinguishes: minutes under an hour, hours under two days, then days, so a slot used ten minutes ago reads `10m` rather than rounding to nothing. `--output=json` carries it as `idle_ms`, with `age_ms` beside the rounded `idle_days` and `created_days`.

**`last branch`** heads the free table's branch column: the branch that held the slot most recently, which is the signal for how warm it will be for similar work, and which [`aspect worktree add`](/docs/cli/worktrees/add) prefers to reuse when you ask for that branch again. It is history rather than somewhere to go — there is no worktree in a free slot — though [`prune`](/docs/cli/worktrees/prune) accepts it as a way of naming the slot.

**`notes`** carries only what is exceptional about a row:

| Note | Meaning |
| - | - |
| `checkout gone` | Leased, but the checkout's directory is gone while git still keeps work for it; `release` says what and how to recover it |
| `clone moved` | Recorded for a clone no longer at its path, and this clone's to take over |
| `another clone's` | Recorded for this clone's path, but the checkout of a clone that was here before it |
| `stranded` | Its clone is gone, and no clone here can take it |
| `other clone` | Belongs to another clone of the repository that still exists |
| `claimed for <branch>`, `add abandoned` | Being set up for that branch; the `add` setting it up has stopped |
| `session resumable`, `session gone` | What the holder's harness says about its session |
| `process stopped` | The holder's recorded process is not running, and its harness cannot say more |
| `subagent <id>` | The lease is a subagent's, under the session in the `session` column |
| `base reclaimed` | The output base was there once and has been collected |
| `leased as <name>` | Git has another branch checked out than the one the lease was taken for, which is still the name `path` and `release` answer to |

**Rows are ordered by what is happening**: in use first, warmest down to cold, then being set up, then — in the free table — the slots you could take, most recently used first. A slot id breaks ties, so a listing you are watching does not reshuffle between runs. The same order applies to `--output=json`.

**Text output is escaped.** Every line printed for a person — tables, warnings, refusal messages — writes a character a terminal would act on or hide as a `\uXXXX` escape, or `\UXXXXXXXX` — eight hex digits — above U+FFFF, whether it comes from a branch name, a file name, a registry field or a hook's error, so it is shown rather than obeyed. A suggested command that names something holding such a character quotes it as `$'…'`, with each such character written as its UTF-8 bytes (`\xHH`), and `'` and `!` as bytes too, so pasted into bash — 3.2, macOS's `/bin/bash`, included — zsh or ksh, it runs on the name as it is. These are the characters [`invalid_agent`](/docs/cli/worktrees/agents#the-layers) refuses in an id; a message's own line breaks and tabs are kept. Under `--output=json` a refusal's `message` is escaped the same way; other values, such as `branch`, `agent` and the files listed, are as they are, escaped only as JSON requires.

## Flags

<ParamField path="--all" type="boolean" default="false">
  List every pool on this machine rather than this repository's, each under its own heading. Works outside a repository too.

  Only the pool belonging to the clone you run from is checked against git, since a worktree listing answers for one object store; the others report the branch their own records hold, and the listing says so. Anything read from disk — whether a slot's directory is still there, how warm its output base is — is current for every pool.
</ParamField>

<ParamField path="--verbose" type="boolean" default="false">
  Also print, for each slot, its directory, the Bazel output base its path derives to (noting when it is not created yet, or has been reclaimed), the pid of any server holding it, and every holder the slot remembers, then each session they name with the command that returns to it.
</ParamField>

<ParamField path="--agent-kind, --agent-id" type="string" default="">
  The session asking, as given to `add` with the same flags, so `sessions[].current` in `--output=json` marks the right one. Needed only when the lease was taken with them; detected automatically otherwise.
</ParamField>

<ParamField path="--output" type="text | json" default="text">
  `text` writes the report to stderr and leaves stdout empty. `json` writes one document to stdout.
</ParamField>

## JSON output

```shell theme={null}
aspect worktree list --output=json
```

```json theme={null}
{
  "schema_version": 1,
  "output_user_root": "/Users/you/Library/Caches/bazel/_bazel_you",
  "pools": [
    {
      "pool": "github.com/acme/repo",
      "remote": "git@github.com:acme/repo.git",
      "reconciled": true,
      "slots": [
        {
          "slot": "18a11aa20e8d",
          "state": "leased",
          "name": "fix/login",
          "reserved_for": "",
          "reservation_abandoned": false,
          "branch": "fix/login",
          "branch_verified": true,
          "detached": "",
          "base": "origin/main",
          "base_sha": "e567ac6b2a18bed22ddf9e220a7e35a62f04e024",
          "agent": {
            "kind": "claude-code",
            "id": "b22fe20c-1111-4000-8000-00000000b22f",
            "pid": 48213,
            "pid_start": "Mon Oct  5 09:14:02 2026",
            "cwd": "/Users/you/src/repo",
            "project": "",
            "source": "env",
            "seen_ms": 1791139816199,
            "harness_id": "b22fe20c-1111-4000-8000-00000000b22f"
          },
          "session_state": "running",
          "process": "running",
          "session_project": "/Users/you/src/repo",
          "resume": "claude --resume b22fe20c-1111-4000-8000-00000000b22f",
          "path": "/Users/you/.aspect/worktrees/github.com/acme/repo/aspect-worktree-18a11aa20e8d",
          "last_branch": "fix/logout",
          "owner": "/Users/you/src/repo/.git",
          "owned_here": true,
          "ownership_known": true,
          "adoptable": false,
          "unlisted": false,
          "stranded": false,
          "idle_days": 0,
          "idle_ms": 1920000,
          "created_days": 12,
          "age_ms": 1041223000,
          "lease_count": 7,
          "history": [
            {
              "name": "fix/logout",
              "agent": { "kind": "claude-code", "id": "b22fe20c-1111-4000-8000-00000000b22f", "cwd": "/Users/you/src/repo" },
              "leased_ms": 1791139814287,
              "released_ms": 1791139815214
            },
            {
              "name": "fix/login",
              "agent": { "kind": "claude-code", "id": "b22fe20c-1111-4000-8000-00000000b22f", "cwd": "/Users/you/src/repo" },
              "leased_ms": 1791139816199,
              "released_ms": 0
            }
          ],
          "had_base": true,
          "output_base": "/Users/you/Library/Caches/bazel/_bazel_you/45acd95442e5ee414bf5c404c316007a",
          "server_pid": 73024,
          "output_base_present": true,
          "warmth": "hot"
        }
      ]
    }
  ],
  "sessions": [
    {
      "kind": "claude-code",
      "id": "b22fe20c-1111-4000-8000-00000000b22f",
      "current": false,
      "project": "/Users/you/src/repo",
      "resume": "claude --resume b22fe20c-1111-4000-8000-00000000b22f",
      "leases": [
        {
          "repo": "github.com/acme/repo",
          "slot": "18a11aa20e8d",
          "path": "/Users/you/.aspect/worktrees/github.com/acme/repo/aspect-worktree-18a11aa20e8d",
          "branch": "fix/login",
          "active": true,
          "held": "1h",
          "held_ms": 3603893,
          "ended_ms": 0,
          "status": "still holding it"
        }
      ]
    }
  ]
}
```

`detached` is the commit-ish a `--detach` slot is at, and `branch` is then empty — `name` carries the `(detached at …)` label the tables print. `base` and `base_sha` say where a branch `add --create` made started: the ref as given, and the commit it was then, which a later fetch does not move.

`state` is `leased`, `free` or `reserved`, which the tables print as `in use`, `free` and `setup`. `warmth` is the `hot`, `warm` or `cold` the tables print, decided from `server_pid` and `output_base_present`. `sessions` is the `--verbose` section as data: one entry per agent session any slot's history names, `current` marking the session asking, each with its leases.

**`pools` is always a list**, of one without `--all`, so `.pools[].slots[]`
reads the same either way and nothing has to branch on the flag the command was
called with. Each pool carries `reconciled`, which says how far its rows were
checked: `true` means git was asked and agrees, `false` means the branch and the
lease are what that pool's own records hold.

`owner` is the git-common-dir of the clone that owns the slot. `git worktree add` is clone-scoped, so a slot belongs to exactly one object store. `owned_here` is that compared against the clone you ran from, which is what decides whether a slot is usable.

`agent` is empty for a worktree taken by a person. `source` says where the identity came from: `flag` for `--agent-kind` / `--agent-id`; `env` for an environment variable — the vendor-neutral pair, a harness's own, or the inferred `AI_AGENT`; `ancestry` for the process tree. `pid_start` is carried beside `pid` because pids are reused and a lease outlives the process that took it.

`session_state` is one of `running`, `resumable`, `gone` or `unknown`, and it
is a claim about the *process* recorded with that lease rather than about the
session: one session can take slots from several processes, so the same session
id can read `running` on one row and `resumable` on another. `resume` is decided
separately, by whether the harness's store holds the session — which is why a
row can be `running` and still offer no command. `process` says whether the
recorded process itself runs: `running`, `stopped`, or `unknown` where none was
recorded, and empty when no agent took the slot.

`session_project` is the directory the harness filed the session under, where
that can be established. `agent.cwd` is only where the command ran, which is a
candidate for the same thing and not a statement about it.

`had_base` records that a Bazel output base was once seen for this slot. It is
what separates a slot nobody has built in from one whose state was reclaimed,
which a lease count cannot: a slot leased twice without a build never had a
base.

`adoptable` is true for a slot recorded for a clone no longer at its path that
this clone would take over — the `clone moved` note. `unlisted` is true for a
slot leased for this clone's path whose checkout this clone's git does not list
— the `another clone's` note. Both are false for a pool this clone does not
own, where git is not asked. `stranded` is true for a slot recorded for a clone
no longer at its path that this clone does not take over — the `stranded` note.
For a pool this clone does not own, where git is not asked, only a leased slot
reads as stranded: a free one is false there, since any clone of the repository
may take it over.

`branch_verified` says whether `branch` came from git or from the pool's own
record. `ownership_known` says whether `owned_here` was established at all —
for a pool this clone does not own there is no clone to compare against, so a
`false` there means "not asked" rather than "somebody else's".

One ordering differs from the table: a slot's `history` is oldest first here,
since that is the order it was written in, while the table shows it newest
first.

`output_base` is derived, not asked for: Bazel names a base `md5(workspace_path)` under the output user root, so the pool can compute it without starting a server. It is where the slot's warm state lives. `output_user_root` at the top of the document is the root it was derived under.

<Note>
  This command takes no lock and only reads, so it is safe to run while another `aspect worktree add` is in flight. It may show a view from a moment ago, never a half-written one.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.