Metadata-Version: 2.4
Name: twig-cli
Version: 0.2.0
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Rust
Classifier: Topic :: Software Development :: Version Control :: Git
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Utilities
Requires-Dist: typer>=0.16
Requires-Dist: rich>=14.0
Requires-Dist: pydantic>=2.11
Requires-Dist: pytest>=8.0 ; extra == 'dev'
Requires-Dist: ruff>=0.6 ; extra == 'dev'
Provides-Extra: dev
Summary: Fast Git worktree CLI — create, jump, prune. Built for humans and agents.
Keywords: git,worktree,cli,agent,typer,pydantic
Author-email: ml-lubich <ml-lubich@users.noreply.github.com>
License-Expression: MIT
Requires-Python: >=3.11
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Documentation, https://github.com/ml-lubich/twig/tree/main/docs
Project-URL: Homepage, https://github.com/ml-lubich/twig
Project-URL: Issues, https://github.com/ml-lubich/twig/issues
Project-URL: Repository, https://github.com/ml-lubich/twig

# 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 `cd`s. Built with Typer, Rich, Pydantic, and a **Rust**
hot path (`twig_core`) for validate / glob / porcelain parse under agent swarms.

[![PyPI](https://img.shields.io/pypi/v/twig-cli.svg)](https://pypi.org/project/twig-cli/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.11%2B-blue.svg)](pyproject.toml)
[![Tests](https://github.com/ml-lubich/twig/actions/workflows/ci.yml/badge.svg)](https://github.com/ml-lubich/twig/actions/workflows/ci.yml)

## Install

```bash
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

```bash
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](https://github.com/raghavpillai/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.

```bash
twig agent guide             # markdown playbook for LLM/automation callers
twig agent schema            # JSON Schema for every result type + TwigConfig
```

```bash
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.

```json
{
  "$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:

```bash
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

```bash
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/`](docs): [`OVERVIEW.md`](docs/OVERVIEW.md),
[`ARCHITECTURE.md`](docs/ARCHITECTURE.md), [`DESIGN.md`](docs/DESIGN.md),
[`API.md`](docs/API.md), [`TESTING.md`](docs/TESTING.md),
[`REQUIREMENTS.md`](docs/REQUIREMENTS.md), [`DEPLOYMENT.md`](docs/DEPLOYMENT.md).

## License

MIT — see [`LICENSE`](LICENSE).

