workshell
v0.5.1
Published
Agent- and human-friendly Git multitasking, powered by worktrees
Maintainers
Readme
Install
$ npm i -g workshell # aliases to `wk`
$ wk --help
wk v0.5.0 - Open branches in ephemeral subshells
Usage: wk <command> [options]
# ...How workshell works
There have been many attempts to nail a DX for parallel work in the agentic coding era. Most are thin wrappers over git worktree. Instead wk introduces a new paradigm: the workshell.
A workshell is an ephemeral worktree whose lifecycle is bound to a subshell.
Here's how it works (key points in bold).
- You open a Git branch with
wk open <branch>or create a new one withwk new <branch>. - An ephemeral worktree is created for this branch (in
~/.workshell/worktrees) and opened in a fresh subshell. - You are now in a 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
wk close. 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.
Features
This approach has some nice properties.
- 🖥️ Tab-local workspaces — Normally a
git checkout/git switchchanges your active branch for all terminals. With workshells, you can open branches in the current tab only. - 🌳 Full isolation — Each workshell is isolated on disk, so the changes you make don't interfere with anything else you're doing.
- 📦 Instant setup — Common config files are automatically copied using copy-on-write, and your package manager is auto-detected so
npm/yarn/pnpm/buninstall runs automatically. - 🙅♂️ Never stash again — You can
wk opena branch even with uncommitted changes. When you exit the subshell, things will be exactly the same as they were. ☕️ - Consistent with branch semantics — As with regular
git switch,wk closewon'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 workshells so multiple agents can work simultaneously without conflicts.
Quickstart
This section is entirely linear and self-contained. Try running all these commands in order to get a feel for how wk works. First, install wk.
$ npm i -g workshellThis installs the workshell CLI. For convenience, it's also aliased to wk. We'll use wk throughout the quickstart.
$ wk status
branch: main (root)
worktree: /Users/colinmcd94/Documents/projects/pf
status: cleanClone a repo (any repo works):
$ git clone [email protected]:colinhacks/zod.git
$ cd zodAfter cloning, the main branch is checked out. Let's say we want to start work on a new feature:
$ wk new feat-1
✓ feat-1 (from main)
Opening branch in ephemeral subshell
Type 'wk close' to return.You're now in a workshell. Check where you are:
$ pwd
~/.workshell/worktrees/{repo-id}/zod@feat-1
$ git branch --show-current
feat-1Now let's make some changes. (You can also open the repo in an IDE, start an agent run, etc.)
$ touch a.txtNow let's try to close the workshell.
$ wk close
⚠ Uncommitted changes found. Commit, stash, or reset your changes first.
Or use --force/-f to discard changes
wk close -fWe aren't able to close because we have uncommitted changes. Let's commit them.
$ 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, wk offers to auto-merge the changes.
$ wk 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'CLI
wk v0.x.y - Human- and agent-friendly Git multitasking
Usage: wk <command> [options]
Commands:
new [branch] Create a branch and open it [--from <branch>]
open <ref> Open a branch or PR in a workshell
close Exit current workshell
ls List orphaned worktrees
status Show current branch
rm <branch> Remove a branch's worktree
config Show or create config file
Options:
--help, -h Show help
--version, -v Show versionList orphaned worktrees
Normally the worktree will be auto-pruned when you close its associated workshell. If a worktree is left behind for some reason, you can list them with wk ls.
$ wk ls
┌────────┬───────────┬───────────────┐
│ branch │ status │ created │
├────────┼───────────┼───────────────┤
│ main * │ clean │ - │
│ feat-1 │ 1 changed │ 5 minutes ago │
└────────┴───────────┴───────────────┘Open a branch or PR in a workshell
You can open any existing Git branch in a workshell.
$ wk open feat-1
✓ feat-1 (existing worktree)
Type 'wk close' to return.You can also open GitHub pull requests directly. wk open accepts anything that gh pr view understands — PR numbers, URLs, or branch names. The PR branch is automatically fetched if it doesn't exist locally. Requires GitHub CLI (gh).
# by PR number
$ wk open 42
# by GitHub URL
$ wk open https://github.com/org/repo/pull/42
# by branch name (tries local branch first, then GitHub PR)
$ wk open feature/authShow current branch status
$ wk status
branch: main (root)
worktree: /path/to/zod
status: cleanRemove a worktree
Remove the worktree for a branch (the branch itself is kept):
$ wk rm feat-1
✓ Pruned worktree for feat-1Closing a workshell
This closes the current workshell and auto-prunes the associated worktree. Your changes survive in your branch commits.
$ wk 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/uncommitted changes. Use the --force/-f flag to force close the workshell; this will discard uncommitted changes.
$ wk close --forcePrint or create a workshell.toml
To print or create a config file:
$ wk config
✓ Config file: /path/to/repo/.git/workshell.toml
────────────────────────────────────────────────────────────
setup = "npm install"
────────────────────────────────────────────────────────────If no config exists, you'll be prompted to create one.
$ wk config
No config file found.
Where would you like to create one?
1. .git/workshell.toml (local only, not committed)
2. workshell.toml (project root, can be committed)
Choice (1/2): 1
✓ Created /path/to/repo/.git/workshell.tomlAutomatic file copying
When you create a workshell, wk automatically copies common config files from your repo to the new worktree using copy-on-write. This includes env files, package manager config, version manager config (asdf, mise, nvm), and editor overrides.
.env*
.npmrc*
.yarnrc*
.pnpmfile.*
.pnpmrc*
.tool-versions
.node-version
.nvmrc
.ruby-version
.python-version
.mise*.toml
.dir-locals.el
.editorconfig.localCopy-on-write is near-instant and uses zero extra disk space (until files are modified). This works automatically on macOS (APFS) and Linux (Btrfs/XFS). On other filesystems, files are copied normally.
You can override which gitignored files get copied with the copy option in workshell.toml:
# override default patterns (default: .env*, .npmrc*, .nvmrc, .tool-versions, etc.)
copy = [".env*", ".secret*", "data/fixtures/**"]Patterns without / match at any depth (e.g. .env* matches both .env and packages/api/.env.local). Patterns with / are anchored to the repo root. Full glob syntax is supported (*, **, ?, [abc], etc.).
Package manager detection
wk auto-detects your package manager and runs install automatically:
| Lockfile | Command |
|----------|---------|
| pnpm-lock.yaml | pnpm install |
| yarn.lock | yarn install |
| bun.lockb / bun.lock | bun install |
| package-lock.json | npm install |
| uv.lock | uv sync |
| poetry.lock | poetry install |
| Pipfile.lock | pipenv install |
| pdm.lock | pdm install |
Detection requires the binary to be installed. If the install command fails, a warning is printed and setup continues. Mixed JS+Python projects are supported.
Setting a setup script in workshell.toml disables automatic package manager detection. Include the install command in your setup script if you want both:
setup = "pnpm install && nvm use"workshell.toml
You can optionally configure wk with a workshell.toml file.
wk looks for config files in the following order:
| Path | Description |
|------|-------------|
| .git/workshell.toml | Local only, not committed (highest precedence) |
| workshell.toml | Project root, can be committed |
Options
# optional: override which gitignored files get copied (default: .env*, .npmrc*, .nvmrc, etc.)
copy = [".env*", ".secret*"]
# optional: setup script executed in subshell after initialization
# disables automatic package manager detection (see above)
# variable substitutions:
# `{{ branch }}` — name of opened branch, e.g. `feature/auth`
# `{{ repo_path }}` — absolute path to main repo, e.g. `/path/to/repo`
# `{{ worktree_path }}` — absolute path to worktree, e.g. `~/.workshell/worktrees/.../repo@feat`
setup = "nvm use"