Agent- and human-friendly Git multitasking, powered by worktrees
npm install git-wisp
git-wispHuman- and agent-friendly Git multitasking. Powered by worktrees.
by @colinhacks
``bash`
$ npm i -g git-wisp # aliased to wisp
worksThere have been many attempts to nail a DX for parallel work in the agentic coding era. Most are thin wrappers over git worktree. Instead, this tool introduces a new paradigm: the wisp.
A _wisp_ is a worktree in an isolated subshell that is pruned-on-close.
Here's how it works (key points in bold).
- You open a Git branch with wisp open or create a new one with wisp new ..git/wisp/worktrees
- An ephemeral worktree is created for this branch (in ) and opened in a fresh subshell.wisp close
- You are now in an fresh checkout of your repo that is isolated on disk. Make changes with your agent/editor of choice and commit them.
- You close the subshell with . The associated worktree is auto-pruned.
- Your changes still exist on the associated branch, as Git commits/branches are shared among all worktrees. The worktree is destroyedβbut your commits aren't.
This approach has some nice properties.
- π₯οΈ Tab-local checkouts β Normally a git checkout/git switch changes your active branch for all terminals. With wisps, you can functionality open branches in the current tab only.wisp open
- π³ Full isolation β Each wisp is isolated on disk, so the changes you make don't interfere anything else you're doing.
- π
ββοΈ Never stash again β You can a branch even with uncommitted changes. When you exit the subshell, things will be exactly the same as they were. βοΈgit switch
- πͺΎ Consistent with branch semantics β As with regular , wisp close won't let you close the subshell if you have unstaged/uncommitted changes. This is a feature, not a bug! Regular worktrees make it easy to lose your work in a forgotten corner of your file system.
- π€ Agent-ready β Spin up parallel wisps so multiple agents can work simultaneously without conflicts.
This section is entirely linear and self-contained. Try running all these commands in order to get a feel for how wisp works. First, install wisp.
`bash`
$ npm i -g git-wisp
Then clone a repo (any repo works):
`bash`
$ git clone git@github.com:colinhacks/zod.git
$ cd zod
After cloning, the main branch is checked out. Let's say we want to start work on a new feature:
`bash
$ wisp new feat-1
β feat-1 (from main)
Opened branch in ephemeral subshell at .git/wisp/worktrees/zod@feat-1
Type 'wisp close' to return.
`
You're now in a wisp. Check where you are:
`bash
$ pwd
/Users/colinmcd94/Documents/repos/zod/.git/wisp/worktrees/zod@feat-1
$ git branch --show-current
feat-1
`
Now let's make some changes. (You can also open the repo in an IDE, start an agent run, etc.)
`bash`
$ touch a.txt
Now let's try to close the wisp.
`bash`
$ wisp close
β Uncommitted changes found. Commit, stash, or reset your changes first.
Or use --force/-f to discard changes
wisp close -f
We aren't able to close because we have uncommitted changes. Let's commit them.
`bash`
$ git add -A && git commit -am "Add a.txt"
Now we can try closing again. Since our changes can be fast-forwarded from the base branch, wisp offers to auto-merge the changes.
`bash
$ wisp close
β Back in main
Pruned worktree. Your changes are still in the 'feat-1' branch.
To merge your changes:
git merge feat-1
Auto-merge? (y/n/never) y
β Merged 'feat-1' into 'main'
`
`sh
wisp v0.x.y - Human- and agent-friendly Git multitasking
Usage: wisp
Commands:
new [branch] Create a branch and open it [--from
open
close Exit current wisp
ls List orphaned worktrees
status Show current branch
rm
config Show or create config file
Options:
--help, -h Show help
--version, -v Show version
`
Normally the worktree will be auto-pruned when you close its associated wisp. If a worktree is left behind for some reason, you can list them with wisp ls.
`sh`
$ wisp ls
ββββββββββ¬ββββββββββββ¬ββββββββββββββββ
β branch β status β created β
ββββββββββΌββββββββββββΌββββββββββββββββ€
β main * β clean β - β
β feat-1 β 1 changed β 5 minutes ago β
ββββββββββ΄ββββββββββββ΄ββββββββββββββββ
You can open any existing Git branch in a wisp.
`sh
$ wisp open feat-1
β feat-1 (existing worktree)
Type 'wisp close' to return.
`
`sh`
$ wisp status
branch: main (root)
worktree: /path/to/zod
status: clean
Remove the worktree for a branch (the branch itself is kept):
`sh
$ wisp rm feat-1
β Pruned worktree for feat-1
`
This closes the current wisp and auto-prunes the associated worktree. Your changes survive in your branch commits.
`sh
$ wisp close
β Back in main
Pruned worktree. Your changes are still in the 'feat-1' branch.
To merge your changes:
git merge feat-1
Auto-merge? (y/n/never) y
β Merged 'feat-1' into 'main'
`
If the branch hasn't been pushed to a remote, you'll be prompted to auto-merge:
- y β merge into base branchn
- β skip (branch is kept, merge manually later)never
- β permanently disable auto-merge prompt
That command will fail if you have unstaged/uncommited changes. Use the --force/-f flag to force close the wisp; this will discard uncommitted changes.
`sh`
$ wisp close --force
To print or create a config file:
`sh
$ wisp config
β Config file: /path/to/repo/.git/wisp.toml
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
setup = "npm install"
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
`
If no config exists, you'll be prompted to create one.
`sh
$ wisp config
No config file found.
Where would you like to create one?
1. .git/wisp.toml (local only, not committed)
2. wisp.toml (project root, can be committed)
Choice (1/2): 1
β Created /path/to/repo/.git/wisp.toml
`
You can configure wisp with a wisp.toml file. This is useful for running setup scripts when opening a wisp (e.g., npm install).
wisp looks for config files in the following order:
| Path | Description |
|------|-------------|
| .git/wisp.toml | Local only, not committed (highest precedence) |wisp.toml
| | Project root, can be committed |
`toml`setup script executed in subshell after initialization
setup = "npm install && cp {{ repo_path }}/.env {{ worktree_path }}/.env"
The following variable substitutions are supported in setup.
| Variable | Description | Example |
|----------|-------------|---------|
| {{ branch }} | Branch name | feature/auth |{{ repo_path }}
| | Absolute path to main repo | /path/to/repo |{{ worktree_path }}
| | Absolute path to worktree | /path/to/repo/.git/wisp/worktrees/repo@feat` |