The problem is not creating worktrees, but managing them
When two or more agents work in parallel on different features, a problem
appears that Git does not solve by itself: how to manage changes so the agents
do not interfere with one another.
One possible strategy is agent-to-agent communication. Agents can delegate
tasks, transfer context, declare ownership, and perform handoffs. This helps
coordinate intent and dependencies, but it is not physical isolation. If one
agent has to wait for another agent's response, contract, or commit before it
can continue, the supposed parallelism can become a sequence of coordinated
steps.
Claude Code's documentation makes this distinction explicit: agent teams can
communicate and share tasks, while worktrees are used when changes to files
need to be isolated.
Worktrees complement that strategy. Each agent works in its own working tree
and can modify files without directly overwriting another agent's checkout.
This reduces interference during editing, but it does not eliminate semantic
conflicts: two agents can still design incompatible changes or depend on
different interfaces.
Communication does not disappear either. It may still be necessary to
coordinate a contract, a commit, a merge, or a later integration.
That raises another question: what happens after a worktree is created, and
how is its lifecycle managed?
An isolated worktree has no lifecycle by itself
Git provides worktrees, but it does not define how their entire lifecycle
should be managed.
A first management layer can be found in the
obra/superpowers/using-git-worktrees
skill. It detects whether the agent is already inside a worktree, distinguishes
a submodule from a worktree, selects a location, verifies that the directory
is ignored, creates the worktree, prepares dependencies, and runs a test
baseline.
That flow solves provisioning and initial setup. Operational questions still
remain: how is the work integrated? What happens when a merge fails? How do we
avoid removing a branch with an unexpected state? What happens if the process
dies during shutdown? How are local changes in the destination recovered?
That is the space worktree.sh attempts to cover: not replacing Git's
worktree creation, but adding a lifecycle layer for agents that create, use,
integrate, and abandon work environments in an automated way.
worktree.sh: a lifecycle for worktrees
worktree.sh is a tool for managing the lifecycle of worktrees. Its
parent-only interface includes these operations:
-
create: creates and validates the worktree and its working branch. -
complete: verifies the state, performs the squash merge, and removes resources when the closing conditions are met. -
cleanup: removes the worktree without integrating it; destructive discard requires--discard. -
recover: resumes pendingpreserve-dirtytransactions. -
prune: cleans up Git's worktree records and reports unassociated directories. -
list: shows the worktrees registered by Git.
The tool also adds ID validation, base-branch selection, predictable paths and
branches, locks for incompatible operations, and journals that record closing
phases.
An agent's workflow
The workflow starts when the agent receives a work-id. From that ID,
worktree.sh creates a separate branch and working tree from the detected base
branch or from the branch provided explicitly.
work request ↓ create worktree ↓ agent modifies isolated files ↓ prepare / verify, when configured ↓ complete or cleanup A minimal way to start work is:
.flowtask/scripts/worktree.sh create hotfix/test-001 --base main The agent works inside the worktree without directly modifying the shared
checkout. When it finishes, it can run configured validations and close the
work with:
.flowtask/scripts/worktree.sh complete hotfix/test-001 --base main complete verifies the worktree's identity and state, integrates commits with
a squash merge, and only then removes the branch and worktree when the closing
validations succeed.
If the work must be abandoned without integration, the workflow uses
cleanup:
.flowtask/scripts/worktree.sh cleanup hotfix/test-001 Forced discard requires explicit authorization through --discard.
There is also a more delicate case: the destination may contain local changes
that should not become commits yet. With --preserve-dirty, the tool captures
eligible tracked and untracked changes, temporarily cleans the destination,
performs the squash merge, and then attempts to restore those changes.
Ignored files are not part of that preservation. If restoration encounters a
conflict, the transaction becomes pending_manual: the backup is preserved and
resolution requires human intervention. In that case, recover can resume the
transaction, but it does not resolve the conflict automatically.
What happens when something fails
The lifecycle must also define what happens if a process dies during a merge,
the worktree changes, or restoration finds a different state.
For that purpose, worktree.sh records closing phases in journals. A journal
identifies which steps have been confirmed and from where an operation can be
resumed. It is not merely an informational log: it is part of the state the
tool uses to decide whether it can continue or must stop.
For example, if complete attempts a squash merge and encounters a conflict,
the closing operation stops and preserves the worktree and branch. The tool
does not remove resources that may be needed to investigate or resolve the
conflict.
In a --preserve-dirty transaction, if the process dies after capturing the
destination changes but before restoring them, recover can resume the flow
from the recorded state.
If restoration encounters a conflict, the transaction enters pending_manual,
keeps the backup, and requires human intervention. recover can continue the
reconciliation after the state is corrected, but it does not decide how to
resolve the conflict.
This behavior is deliberate: when the state cannot be verified, the tool
prefers to stop and preserve resources rather than perform an irreversible
action.
The tool's current state
The current manager has a parent-only flow—the main repository creates and
closes its own worktrees—and a submodule flow.
Submodule initialization is explicit: it does not run by default when an agent
creates a worktree. This avoids populating dependencies the agent does not
need.
To act directly on a submodule, target-direct treats the submodule as the
target repository. The flow validates its relationship with the parent
repository, the path, the gitlink (the parent's reference to the submodule
commit), and branch identity.
link-parent is also available for controlled gitlink updates, with the
staged-only mode modifying only the index; it does not commit, merge, or push
the parent.
The tool does not yet expose an MCP interface or subtree integration. Evolution
toward MCP is already included in the project roadmap.
Project evolution
The manager's roadmap is organized into these stages:
1. Stabilize the standalone CLI ↓ 2. Migrate the backend to Python ↓ 3. Keep worktree.sh as a compatibility shim ↓ 4. Expose the backend through MCP The main direction is to separate the manager's core, preserve its guarantees
during the migration, and later provide an interface suitable for agents and
other orchestrators.
The code and the manager's evolution are available in the
worktree-manager repository.



