Repository navigation
Releases: dcolliervb23/warmtree
Release list
v0.9.0 — sweep and slot anchors
Fold everything finished back in — and give agents an anchor that survives their own harness.
warmtree sweep (#21)
One command folds every merged branch's worktree or slot back into the pool:
warmtree sweep --fetch- Taken slots whose branch git certifies as merged are released; hand-made worktrees are adopted and released in one step, folder and dependencies joining the pool.
--fetchfirst, so branches merged only upstream count; a branch folded from its upstream is deleted under that certificate even without a local tracking ref.- Dirty trees, unmerged branches, foreign directories, and slots other live sessions hold are left exactly where they are.
releasenow refuses a directory that is no longer a working tree of this repository, protecting every caller, not just sweep.
warmtree init && warmtree sweep --fetch is now the whole onboarding for a repo with years of worktree clutter. Known honest limit: squash-merged branches are not ancestors of base, so sweep does not detect them.
Slot anchors for agents (#22)
Dogfooding kept hitting the same failure: an agent runs cd "$(warmtree take <branch>)", but agent harnesses reset the working directory between commands, so later commands silently run against the main worktree.
warmtree path <branch>prints the absolute path of the taken slot holding a branch:git -C "$(warmtree path feat/x)" statusworks no matter how often the harness resets.- Repeat takes are idempotent.
warmtree takeof a branch already in a taken slot returns that slot's path again with a stderr note, instead of git's "already checked out" error — and adopts a stale lease so liveness checks see the actual occupant. A live foreign holder is preserved and warned about. - The agent skill and the managed instructions block now lead with path-anchored addressing; the
cdone-liner is demoted to a note for humans in persistent terminals.
Also
take's stderr notes moved into the pool layer so they are always truthful.- README: sweep-based onboarding,
path, and a correction — take claims the most recently used ready slot (MRU, since 0.8.0), not the oldest.
Upgrade: uv tool upgrade warmtree or pipx upgrade warmtree, then warmtree skill to refresh installed copies (unedited copies update automatically; edited ones are kept).
v0.8.0
Honest takes, and a pool that remembers its size. Both born from agent dogfooding reports (#16, #17, and the sizing question that followed).
take honors remote branches (#16). A branch that exists only on origin is now created from the remote and tracks it — matching git checkout's behavior — instead of silently handing back a fresh branch from base wearing the remote branch's name. The old behavior left one push --force-with-lease between an agent and an open PR's commits. When local and origin tips diverge, take says so on stderr and touches nothing.
The pre-warmed claim is now conditional and enforced by nagging (#17). With [warm] run empty, slots are bare checkouts — and now everything says so: the skill states it plainly, fill warns when a known lockfile is present (naming the file and the suggested command across 14 ecosystems, npm to cargo to dotnet), and init prints the same suggestion. Nothing is ever written to the config: warmtree still never guesses your install command.
Bursts no longer become monuments. The pool grows past size under load and, until now, stayed there forever — every burst slot a full node_modules on disk. Surplus ready slots that sit unused for shrink_after (14 days by default, configurable, "off"-able) are now trimmed back to size, oldest idle first, by the same self-maintaining refresh pass. size is the floor: the pool never trims below its configured ready buffer.
The prerequisite: hot allocation. take now claims the most recently used slot instead of round-robining through the oldest — cold surplus actually idles, so it can actually decay, and you get the freshest checkout besides.
Also: refresh --help now documents the full self-maintenance story with a worked example, and trim persists state after every removal so a mid-loop failure cannot orphan the bookkeeping.
v0.7.0
Adoption no longer touches your disk.
Community criticism, accepted in full: moving and renaming a directory at adopt time risked breaking whatever pointed at it — editors, running servers, shell sessions, absolute paths in configs. The original justification did not survive an audit (nothing in the pool actually requires slots to live in the pool directory), so the disruption now lands where it is harmless:
warmtree adopt <path>registers the worktree in place. Original path, original name, nothing on disk changes. Everything pointed at the folder keeps working.warmtree releasegraduates the folder into the pool asslot-N— at the one moment nothing legitimate is inside it: the branch is done, the lease surrendered, and the tree is being reset by release's own semantics anyway. The folder-named-after-a-dead-branch problem never occurs, because the rename coincides with the branch's death.- A blocked move degrades gracefully: the slot stays ready and usable at its old address, and the next release retries.
Two guardrails found while building it:
git worktree moveinto an existing directory does not fail — it silently nests the tree inside and exits zero. The graduation step guards against that explicitly.- A slot living outside the pool directory is never auto-deleted:
trimskips it andremoverefuses without--force, because that folder is in your own space.
The agent skill and README describe the new behavior; installed skill copies refresh automatically on the next warmtree skill or init.
v0.6.0
The pool tends itself.
Auto-refresh. Any take, fill, or status that finds the last refresh older than refresh_every (24 hours by default) spawns a detached background refresh --fetch and returns immediately — the git gc --auto pattern. No daemon, no cron, no waiting: using the pool keeps the pool warm, fetching origin and re-warming slots whose lockfiles changed, while your own checkout is never touched.
Details that make it safe:
- The overdue check-and-claim runs under the pool lock, so overlapping commands cannot double-spawn; a failed spawn hands the duty back instead of suppressing retries.
- A no-op pass over an empty pool does not mark it fresh; a fetch always counts, so an all-taken pool does not spawn busywork.
- Opt out with
refresh_every = "off"in.warmtree.toml, orWARMTREE_AUTO_REFRESH=offin the environment (handy in CI). - A nightly
warmtree refresh --fetchin cron still composes, for slots that should be fresh before the day's first command.
With this, the full loop is automatic: origin moves → the next warmtree command notices → slots land on origin's tip with dependencies current → take branches from there. Local branches are never pulled or moved; that remains yours.
v0.5.0
The trust release: the pool now stays current, honest, and held.
Slots stay current. warmtree refresh --fetch fetches origin and parks slots on the remote tip — no manual pull needed, and your own checkout is never touched. A warm take now branches from the slot's parked position, so a freshly refreshed slot is never dragged back to a stale local base. One cron line (warmtree refresh --fetch) keeps the whole pool tracking origin.
State stays honest. A ready slot that someone checked a branch out in behind warmtree's back is now detected: take refuses to claim it (recording what git says instead), and doctor reports it, with --fix marking it taken to preserve the work. Ownership checks ensure a replaced directory — even one inside the repo — can never be mistaken for a slot.
Slots are held. take and adopt lease the slot to the claiming session; release refuses while a different, still-running session holds it (--force overrides, stale leases self-clear). The race where anything could reset a slot out from under a working shell, editor, or exec is closed for the common case.
Hooks can no longer hang the pool. Pool git operations run with repo hooks disabled — a lingering post-checkout hook (telemetry spawning children) could previously hang take indefinitely. Per-slot setup belongs to [warm] run/copy; [pool] git_hooks = true restores hooks if yours are quick and wanted.
Agents now default to warmtree. warmtree instructions (and init, automatically) maintains a marked prefer-warmtree block in your user-level agent instruction files — Claude Code, Copilot, Codex — so agents reach for the pool instead of git worktree add from habit. Repo-level with --repo. A strong preference by design, not a ban. The skill also gained slot house rules and the default-not-fallback framing.
Also: the README explains when to use warmtree, worktrunk, or both, with a feature comparison.
v0.4.0
The agent-ergonomics release: the slot lifecycle is now fully drivable by orchestrators.
warmtree exec <branch> -- <command...> runs one command inside the slot holding a branch — cwd set, WARMTREE_SLOT in the environment, streams and exit code passed straight through. warmtree exec fix/tests -- npm test behaves like running it there, without the take/cd dance.
warmtree take --scratch claims a slot on a generated scratch/<id> branch for throwaway experiments. The name is picked under the claim lock, so concurrent scratch takes can never collide; the branch name and its release command print on stderr while stdout stays the bare path.
take --json and release --json emit machine-readable results: the take object includes cold, so a harness can tell warm claims from slow fallbacks; the release object reports branch_deleted. Human warnings stay on stderr in both modes.
The agent skill teaches all of it — scratch, json, and when to reach for exec — and every installed copy refreshes automatically on the next warmtree skill or init run, no --force needed.
Also: exec distinguishes its failure modes (127 command-not-found, 126 not-executable, and a broken slot points at warmtree doctor).
v0.3.0
The operations release: pool health, disk visibility, and repo-clean skill installs.
warmtree doctor [--fix] compares state.json against git and the filesystem and reports drift: deleted slot directories, lost git registrations, stuck warming states, untracked directories in the pool. The one automated repair (--fix) is dropping bookkeeping for a directory that no longer exists and no worker owns; anything that might touch in-progress work is report-only with a hint. Exit codes make it cron-able next to your nightly refresh.
warmtree status --du adds a SIZE column and a total, measuring what the pool costs on disk (du_bytes per slot with --json). Opt-in, so plain status stays instant.
warmtree skill --user and warmtree init --user-skill install the agent skill into the tools' personal skills folders (~/.claude/skills, ~/.copilot/skills, ~/.codex/skills, ~/.cursor/skills) instead of the repo — for repos that must never contain, or risk containing, the skill file. One user-level install covers every repo; skill --user also works outside any repo.
Also: skill hints now print runnable commands, and the README documents updating (uv tool upgrade warmtree / uvx self-resolving) and testing feature branches with uvx --from.
v0.2.0
The onboarding release: warmtree now folds your existing worktrees into the pool.
New: warmtree adopt <path> moves a hand-made worktree into the pool as a taken slot. Its branch, uncommitted changes, and installed dependencies come with it, and a later warmtree release recycles them as a warm ready slot instead of the tree being deleted. The branch is never renamed; the directory becomes slot-N. See the new "Already have worktrees?" section in the README.
Also:
- Bare
warmtreeprints the full help instead of a terse argparse error. - The agent skill covers
adopt, with a caution that the move breaks anything open on the old path. - Package metadata now links this changelog and the issue tracker from the PyPI sidebar.
- README: onboarding guidance and a recipe for testing feature branches with
uvx --from.
v0.1.2
warmtree --version now reports the installed version; 0.1.1 misreported itself as 0.1.0.
v0.1.1
Fixes skill refresh: an unedited installed copy of a past SKILL.md version now updates without --force; only user-edited copies are kept. Corrects the SKILL.md example to match size counting ready slots only (taken slots do not count).