Skip to Content

worktree list

List known worktrees managed for the current repository.

Usage

worktree list worktree list --agents worktree list --json worktree list --agents --json

Use this before open, remove, or cleanup decisions.

Flags

FlagAliasDescription
--agents-aShow the agent session living in each worktree
--jsonPrint one JSON document on stdout instead of the list, and run non-interactively

Showing agent sessions

--agents finds the agent sessions that are open and names the one found in each worktree. It asks two sources and joins them on the directory each session runs in, compared as real paths so a symlinked or differently-cased spelling of the same directory still matches:

  • Herdr, through herdr agent list, when herdr is installed. This finds agents Herdr started for you, including ones the runtime does not list a process for.
  • The agent runtime, through <program> agents --json. The program is the first word of agent.command, or the herdr.agent kind when agent.command is unset.

A Herdr agent that matches a runtime session by session id is named by that session; one the runtime does not list is named <kind>@<pane>, for example claude@w5:p1.

- feature/one (Agent: feature-one-1f) - feature/two (Ahead: 2, Behind: 1, Agent: feature-two-1f [waiting]) - feature/three (Agent: notes-1f [interactive]) - feature/four (Agent: feature-four-1f [interactive, waiting]) - feature/five (Agent: feature-five-1f [done])
  • No marker — a background agent that is getting on with its work.
  • [waiting] — a background agent that is live but not progressing: idle between turns or blocked on a question, so it is probably waiting on you.
  • [done] — a session the runtime reports as finished. It no longer holds anything back, so cleanup treats the worktree as ordinary stale work. The exceptions are a session Herdr still shows in a pane and one whose process is still running, which the runtime has been seen to report as finished: those are not marked [done]. A session Herdr shows holds the worktree back only while Herdr reports it as anything other than idle or done. A running one is weighed by its status like any live session: it is [waiting] when it is idle or blocked on a question, and it holds the worktree back unless it is only idle.
  • [interactive] — a session in a terminal rather than a background one: somebody’s own, or an agent worktree branch started through Herdr. It is listed because cleanup will refuse to remove a worktree somebody is working in, and this is where you find out which those are.
  • [interactive, waiting] — an interactive session that is live but not progressing, on the same terms as [waiting]: an agent that has finished its turn or stopped on a question. One that is only idle, rather than blocked on a question, does not hold the worktree back.

A worktree with no Agent: detail has no session in it. Where a worktree holds more than one — a dispatched agent and your own terminal, say — the entry names one of them, and the marker describes the session it names.

Without --agents, list asks neither source anything and its output is unchanged. If neither is available — herdr is not installed and neither agent.command nor herdr.agent is set, or the runtime reports nothing usable — --agents simply lists no sessions rather than failing.

Ahead and behind counts

Ahead: counts commits this worktree carries that its comparison base does not. The count is taken for every worktree whose directory is present, including one on a branch you have never pushed — that is the case worth seeing, because those commits exist in exactly one place.

The base depends on whether the branch tracks a remote:

The branchAhead: counts againstBehind:
tracks a remote branch that still existsits upstreamshown
tracks nothing, or a remote branch that is goneorigin/HEAD, falling back to defaultSourceBranchnot shown

A worktree whose commits are all already applied in that base shows where they landed instead of how many there are:

- feature/bulk-actions (Remote removed, Merged into origin/main)

The count is not hidden to be tidy. It is replaced because it would be misleading: a squash merge lands a branch as one new commit and a rebase rewrites every commit it touches, so a merged branch stays “7 ahead” of main forever while holding nothing that is not already there. Ahead: is in this list to say what removing the worktree would cost, and for that branch the answer is nothing. Where even one commit is not already in the base, the count is shown as usual — the whole change is what is compared, so a branch that has moved on since it was merged counts as carrying work again.

Behind: is only ever counted against an upstream. Against the default branch it would mean “commits on main this branch does not have”, which is true of almost every worktree the moment main moves — so where there is no upstream the figure is left out rather than printed as a 0 that was never counted. A count of zero is not shown either way; only a non-zero figure earns a line.

If neither origin/HEAD nor defaultSourceBranch resolves, the count cannot be taken at all, and the entry says so instead of looking empty:

- feature/orphan (Unpushed commits unknown: no comparison base; set defaultSourceBranch or run git remote set-head origin --auto)

The same detail appears in remove’s picker and in cleanup’s report of what it held back, because all three render a worktree the same way.

JSON output

--json prints exactly one JSON document on stdout, on one line, in place of the list. The spinner lines and any warning go to stderr. It implies a non-interactive run.

{"worktrees":[{"branch":"feature/one","path":"/abs/repo.worktrees/feature/one","current":false,"pathExists":true,"remote":"origin/feature/one","remoteExists":true,"ahead":2,"behind":1,"mergedInto":null,"uncommittedChanges":0,"safeToRemove":false}]}
KeyValue
branch, paththe worktree’s branch and absolute path
currentwhether it is the worktree you are in
pathExistswhether its directory is still on disk
remote, remoteExiststhe upstream the branch tracks, null when it tracks none, and whether that remote branch still exists
ahead, behindthe counts above as numbers
mergedIntothe base whose commits already contain all of this branch’s changes; null when not known to be merged
uncommittedChangesthe number of uncommitted changes; null when the directory is gone, as there is nothing to count
safeToRemovethe verdict cleanup acts on
agentonly with --agents: null when no session lives in the worktree, otherwise {name, sessionId, herdrAgent, live, interactive, waiting}. waiting is true for a live session that is idle between turns or blocked on a question, whether or not it is interactive. herdrAgent is the name Herdr gives the agent, as branch --json reports it in herdr.agent, or the agent’s pane id when Herdr reports no name

A count or flag that could not be taken is null, never 0 and never left out. ahead and uncommittedChanges are null when the directory is gone (and ahead also when no comparison base resolved), behind is null for a branch with no upstream, and inside agent any of sessionId, herdrAgent, live, interactive and waiting is null when the source did not say. A 0 always means the count was taken and is zero.

A failure prints {"error":{"code","message"}} on stdout instead and exits non-zero; the codes are listed under branch.

Last updated on