Skip to main content

twig 🌳

A Git worktree CLI that gets out of your way — and out of your agent's way too.

Create, jump, and clean up worktrees in one command each. Every command speaks JSON. A real shell hook actually cds. Built with Typer, Rich, Pydantic, and a Rust hot path (twig_core) for validate / glob / porcelain parse under agent swarms.

PyPI License: MIT Python Tests

Install

pipx install twig-cli          # recommended — isolated, puts `twig` on PATH
# or
pip install twig-cli
# or
brew tap ml-lubich/twig https://github.com/ml-lubich/twig && brew install twig
# or from source
git clone https://github.com/ml-lubich/twig && cd twig && pip install -e .

Package name on PyPI is twig-cli; the command you run is still twig. Not on PyPI/Homebrew yet? Install from source — same CLI either way.

Requires Python 3.11+ and git on PATH.

30 seconds

cd your-repo
twig init                        # write .twig.json
eval "$(twig hook zsh)"          # add to ~/.zshrc for real cd

twig new feat-auth               # branch + worktree, source = current branch
twig ls                          # rich table of every worktree
twig go feat-auth                # cd, for real, in your current shell
twig status                      # ahead/behind + dirty, at a glance
twig rm feat-auth                # clean up

Why twig over Branchlet

Branchlet is a solid Ink/React TUI for worktrees. Twig is a CLI-first tool instead — built to be typed, scripted, and driven by agents, not just clicked through.

twig branchlet
Stack Python · Typer · Rich · Pydantic · Rust (PyO3) React · Ink · Zod (~5k LOC UI)
Default UX Scriptable CLI (new / ls / rm / go) Interactive TUI first
Shell cd First-class: twig go + eval "$(twig hook)" Wrapper only, for the bare menu pick
Agent / CI --json on every command, plus twig agent schema + twig agent guide Partial (list --json)
Agent swarms Repo flock, batch-new, foreach -J N, parallel status Not supported
Jump by name twig go / twig which No dedicated jump
Run commands twig exec (one worktree) / twig foreach -J (parallel) Not supported
Health checks twig doctor — git, config, orphans, rust core Not supported
Status twig status — ahead/behind + dirty, all worktrees Not supported
From any ref twig from-ref <branch|tag|commit> Not supported
Lock / rename twig lock/unlock, twig rename Not supported
Defaults Source = current branch, branch = name — one argument Requires -n and -s non-interactively
Editor open twig open + config editor terminalCommand after create only
Completions Native Typer shell completions Custom zsh/bash blocks
Config .twig.json + ~/.twig/config.json, Pydantic-validated, Branchlet keys accepted .branchlet.json
Correctness 878-case edge catalog (incl. swarm/lock/batch) enforced by pytest —

Bottom line: Branchlet is a worktree menu. Twig is a worktree tool — scriptable, agent-ready, and covered by a test catalog most CLIs don't bother with.

Built for agents

Every twig command that produces output supports --json. Human-formatted tables and colors go to the terminal; the same data is always available as structured JSON for scripts, CI, and LLM agents.

twig agent guide             # markdown playbook for LLM/automation callers
twig agent schema            # JSON Schema for every result type + TwigConfig
twig new feat-x --json | jq -r .path
twig batch-new agent-a agent-b agent-c --json    # one lock, many worktrees
twig ls --json
twig status --json
twig doctor --json                              # exits 1 on real problems
twig exec feat-x --json -- npm test              # one worktree
twig foreach -J 8 --json -- npm test             # parallel across worktrees

Swarm-safe by default: mutating commands take a repo flock so parallel agents don't race git worktree add. Pure-Python fallbacks always work; with a Rust toolchain, pip install builds twig_core for faster validate/glob/porcelain.

Conventions an agent (or script) can rely on:

  • Paths and --json payloads → stdout. Human hints and progress → stderr.
  • twig go prints TWIG_CD <path> (or {"path": ...} with --json) — the shell hook turns that into a real cd; scripts can parse it directly.
  • Non-zero exit code on any failure, with twig: <message> on stderr.
  • twig exec/foreach flags (--json, --capture, --all, --continue, -J) go before the literal --; everything after it is passed to the wrapped command verbatim.

Commands

Command What it does
twig new <name> [-s source] [-b branch] Create a worktree, copy config files, run postCreate
twig batch-new <names...> Create many worktrees under one repo lock (agent swarms)
twig from-ref <ref> [name] Create a worktree from any branch, tag, or commit
twig ls List worktrees (rich table or --json)
twig status Ahead/behind vs. default branch + dirty state, for every worktree
twig rm <name> [-f] [--delete-branch] Remove a worktree (and optionally its branch)
twig rename <old> <new> Rename a worktree directory and its git metadata
twig go [name] Emit a cd target; real cd via the shell hook
twig which <name> Print a worktree's absolute path
twig open [name] [-e cmd] Open a worktree in your editor
twig lock <name> [-r reason] / twig unlock <name> Protect a worktree from removal/prune
twig exec <name> -- <cmd...> Run a command inside one worktree
twig foreach [-J N] [--all] [--continue] -- <cmd...> Run a command across worktrees (parallel with -J)
twig prune git worktree prune
twig doctor Diagnose git/repo/config health; exits 1 on real problems
twig init Write a default .twig.json
twig config Show the effective merged config
twig branches List local branches, newest first
twig hook [zsh|bash|fish] Print the shell integration script
twig agent guide / twig agent schema Playbook / JSON Schema for automation

Aliases: create/add → new, list → ls, delete/remove → rm, cd/jump → go, path → which. Add --json/-j to any of the above for machine-readable output. Run twig --install-completion for shell completions.

Configuration

Twig loads config in order (later wins): built-in defaults → ~/.twig/config.json → .twig.json in the repo root. Every field is validated by a Pydantic model — invalid config fails loudly, not silently.

{
  "$schema": "./schema.json",
  "copyPatterns": [".env*", ".vscode/**", ".cursor/**"],
  "copyIgnores": ["**/node_modules/**", "**/dist/**", "**/.git/**"],
  "pathTemplate": "../$BASE_PATH.trees",
  "postCreate": ["uv sync"],
  "editor": "cursor .",
  "deleteBranch": true
}
Field Type Default Meaning
copyPatterns string[] [".env*", ".vscode/**", ".cursor/**"] Glob patterns copied into new worktrees
copyIgnores string[] ["**/node_modules/**", "**/dist/**", "**/.git/**", ...] Glob patterns excluded from copy
pathTemplate string "../$BASE_PATH.trees" Where new worktrees are created
postCreate string[] [] Commands run after creating a worktree
editor string "" Default command for twig open
deleteBranch bool false Delete the branch by default on twig rm

Template vars available in pathTemplate/postCreate/editor: $BASE_PATH, $WORKTREE_PATH, $BRANCH_NAME, $SOURCE_BRANCH, $NAME. Branchlet's .branchlet.json field names are accepted as aliases, so migrating is a rename.

Run twig init to write the default file, twig config to see what's actually in effect, and twig agent schema for the full JSON Schema.

Shell hook

twig go alone can't change your shell's working directory — no subprocess can. The hook wraps twig in a shell function that intercepts its TWIG_CD marker and runs a real cd. Install it once:

echo 'eval "$(twig hook zsh)"' >> ~/.zshrc && source ~/.zshrc
# bash: twig hook bash   ·   fish: twig hook fish

Everything else — ls, status, new, JSON output — passes straight through untouched.

Development

git clone https://github.com/ml-lubich/twig && cd twig
pip install -e ".[dev]"
pytest                    # 547-case edge catalog + unit/integration/CLI suites
ruff check src tests

Docs live in docs/: OVERVIEW.md, ARCHITECTURE.md, DESIGN.md, API.md, TESTING.md, REQUIREMENTS.md, DEPLOYMENT.md.

License

MIT — see LICENSE.

Release files for twig-cli 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for twig-cli 0.2.0
File Size Uploaded
twig_cli-0.2.0.tar.gz 10.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for twig-cli 0.2.0
File Interpreter ABI Platform
twig_cli-0.2.0-cp314-cp314-macosx_11_0_arm64.whl CPython 3.14 CPython 3.14 macOS 11.0+ ARM64 Details

Total release size: 724.5 kB

Release files / twig_cli-0.2.0.tar.gz

Download URL twig_cli-0.2.0.tar.gz
Size 10.4 kB
Tags Source
SHA-256 checksum
How to use checksums
384d99663af837abce8192fc03c127d3777aee53668f08d876ffef85fc71f082
BLAKE2b-256 checksum
How to use checksums
989a0379b91fc933fa5543977129c4d7491a1b3396f6347e8f9feb969aca8808
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.14.1

Release files / twig_cli-0.2.0-cp314-cp314-macosx_11_0_arm64.whl

Download URL twig_cli-0.2.0-cp314-cp314-macosx_11_0_arm64.whl
Size 714.1 kB
Tags CPython 3.14 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
3dbc2ca6c1e33bde8336876326330f096314b7369925632ec7ce8e8bfcae1cb2
BLAKE2b-256 checksum
How to use checksums
464e0e47e8fbd92274e49feaa8488b640ec3835e9ae00d32d605e1678fd89995
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.14.1

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page