Skip to content

feat: list git worktrees the app did not create, read-only (/api/worktrees returns [] for them) #1879

Description

@shohei-sasaki835

GET /api/worktrees returns only worktrees under the managed root
(~/.mulmoterminal/worktrees/<repo>-<hash>/), so a repository whose worktrees were created by
Claude Code's own worktree switch or by hand answers with an empty list even when git worktree list
shows twenty. The worktree overlay is then empty, and there is no way to reach a worktree's
conversation from the UI. Proposal: return them all with a managed flag and let the UI withhold
the destructive actions.

What happens

A repository with 20 worktrees under <repo>/.claude/worktrees/… and <repo>/.worktrees/…:

$ git worktree list | wc -l
20
$ curl -s "localhost:34567/api/worktrees?cwd=<repo>"
{"isGit":true,"base":"develop","worktrees":[]}

Expected: the worktrees the repository actually has, whoever created them.

Why

isManagedWorktree (server/git/worktrees.ts) exists as a delete guard — its comment says so:
"the guard that stops a delete request from touching anything we didn't create". worktree-routes.ts:46
uses that same predicate to decide what to list. Those are different questions. "May this be
removed" has to stay narrow; "does this repository have this worktree" does not.

The cost is not cosmetic. After a restart tmux is gone, and the one thing that can offer a worktree's
conversation back is a row for that directory: dirSession already finds it (its transcripts are
under the worktree's own project directory, and /api/sessions?cwd=<worktree> lists them today).
The overlay simply never asks, because the worktree is not in the list. Saving each worktree as a
cwdPresets chip works as a workaround, which is what makes the missing piece feel like just the
listing.

Proposal

git worktree list --porcelain is already parsed for the whole repository (parseWorktreeList), so
the filter is the only thing dropping the rest.

  • Return every worktree, each with managed: boolean.
  • The overlay shows all of them; remove / push / PR stay behind managed, so the guard's actual
    job is untouched and an unmanaged worktree is read-only — MulmoTerminal did not create it and
    should not offer to delete it.
  • worktreeOccupancy (server/session/worktree-session-limit.ts) keeps limiting managed worktrees
    only. It already says "Only a MANAGED worktree is limited. An ordinary directory takes as many
    terminals as the user wants", and an unmanaged worktree is somebody else's convention — a repo
    that puts them in .worktrees/ may well want two cells in one.

Files I would expect to touch: server/git/worktrees.ts (the listing, and a managed field on
WorktreeInfo), server/git/worktree-routes.ts, and the overlay component that renders the rows.

Open question: whether a read-only row is worth it at all, or whether the answer the maintainers
prefer is "use the app's own worktree feature". A repository whose worktree layout is fixed by its
own tooling cannot move to the managed root, which is the case this is filed from.

Environment

  • MulmoTerminal 4.10.1, started with npx mulmoterminal
  • macOS 26.6.2, arm64, Node v24.19.0, zsh
  • tmux 3.7b, Claude Code 2.1.247
  • Browser: Chrome

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions