Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</p>

<p align="center">
<strong>Parallel development in tmux* with git worktrees</strong>
<strong>Parallel development in tmux* with git worktrees & jj workspaces</strong>
</p>

<p align="center">
Expand All @@ -20,7 +20,7 @@
---

Giga opinionated zero-friction workflow tool for managing
[git worktrees](https://git-scm.com/docs/git-worktree) and tmux windows as
[git worktrees](https://git-scm.com/docs/git-worktree) (or [jj workspaces](https://jj-vcs.github.io/jj/latest/working-copy/#workspaces)) and tmux windows as
isolated development environments. Perfect for running multiple AI agents in
parallel without conflict.

Expand Down Expand Up @@ -1920,12 +1920,16 @@ entire process and pairs each worktree with a dedicated tmux window, creating
fully isolated development environments. See
[Before and after](#before-and-after) for how workmux streamlines this workflow.

**Using jj?** workmux also supports [jj (Jujutsu)](https://jj-vcs.github.io/jj/) natively. jj workspaces provide the same parallel development benefits. workmux auto-detects your VCS backend.

## Git worktree caveats

While powerful, git worktrees have nuances that are important to understand.
workmux is designed to automate solutions to these, but awareness of the
underlying mechanics helps.

> **Note:** These caveats are specific to git worktrees. If you're using jj, some (like ignored files and conflicts) still apply to jj workspaces, while git-specific ones (like `.git/info/exclude`) do not.

- [Gitignored files require configuration](#gitignored-files-require-configuration)
- [Conflicts](#conflicts)
- [Package manager considerations (pnpm, yarn)](#package-manager-considerations-pnpm-yarn)
Expand Down Expand Up @@ -2242,7 +2246,7 @@ workmux completions fish | source
## Requirements

- Rust (for building)
- Git 2.5+ (for worktree support)
- Git 2.5+ (for worktree support) or [jj](https://jj-vcs.github.io/jj/) (Jujutsu)
- tmux (or an alternative backend)

### Alternative backends
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export default defineConfig({
{ text: "Session mode", link: "/guide/session-mode" },
{ text: "direnv", link: "/guide/direnv" },
{ text: "Monorepos", link: "/guide/monorepos" },
{ text: "Git worktree caveats", link: "/guide/git-worktree-caveats" },
{ text: "Worktree caveats", link: "/guide/git-worktree-caveats" },
{ text: "Nix", link: "/guide/nix" },
],
},
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ windows:

### File operations

New worktrees are clean checkouts with no gitignored files (`.env`, `node_modules`, etc.). Use `files` to automatically copy or symlink what each worktree needs:
New worktrees are clean checkouts with no ignored files (`.env`, `node_modules`, etc.). Use `files` to automatically copy or symlink what each worktree needs:

```yaml
files:
Expand Down
20 changes: 11 additions & 9 deletions docs/guide/git-worktree-caveats.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
---
description: Common git worktree pitfalls and how workmux handles them
description: Common worktree pitfalls and how workmux handles them
---

# Git worktree caveats
# Worktree caveats

While powerful, git worktrees have nuances that are important to understand. workmux is designed to automate solutions to these, but awareness of the underlying mechanics helps.
While powerful, worktrees (git worktrees and jj workspaces) have nuances that are important to understand. workmux is designed to automate solutions to these, but awareness of the underlying mechanics helps.

## Gitignored files require configuration
> **Note:** Most caveats below apply to both git worktrees and jj workspaces. Sections that are git-specific are marked as such.

When `git worktree add` creates a new working directory, it's a clean checkout. Files listed in your `.gitignore` (e.g., `.env` files, `node_modules`, IDE configuration) will not exist in the new worktree by default. Your application will be broken in the new worktree until you manually create or link these necessary files.
## Ignored files require configuration

When a new worktree is created (via `git worktree add` or `jj workspace add`), it's a clean checkout. Ignored files (e.g., `.env` files, `node_modules`, IDE configuration) will not exist in the new worktree by default. Your application will be broken in the new worktree until you manually create or link these necessary files.

This is a primary feature of workmux. Use the `files` section in your `.workmux.yaml` to automatically copy or symlink these files on creation:

Expand Down Expand Up @@ -36,9 +38,9 @@ panes:

## Conflicts

Worktrees isolate your filesystem, but they do not prevent merge conflicts. If you modify the same area of code on two different branches (in two different worktrees), you will still have a conflict when you merge one into the other.
Worktrees isolate your filesystem, but they do not prevent merge conflicts. If you modify the same area of code on two different branches (in two different worktrees), you will still have a conflict when you merge one into the other. This applies to both git and jj.

The best practice is to work on logically separate features in parallel worktrees. When conflicts are unavoidable, use standard git tools to resolve them. You can also leverage an AI agent within the worktree to assist with the conflict resolution.
The best practice is to work on logically separate features in parallel worktrees. When conflicts are unavoidable, use standard VCS tools to resolve them (`git` conflict resolution or `jj resolve`). You can also leverage an AI agent within the worktree to assist with the conflict resolution.

## Package manager considerations (pnpm, yarn)

Expand Down Expand Up @@ -69,7 +71,7 @@ rustc-wrapper = "sccache"

This caches compiled dependencies globally, so new worktrees benefit from cached artifacts without any lock contention.

## Symlinks and `.gitignore` trailing slashes
## Symlinks and `.gitignore` trailing slashes (git-specific)

If your `.gitignore` uses a trailing slash to ignore directories (e.g., `tests/venv/`), symlinks to that path in the created worktree will **not** be ignored and will show up in `git status`. This is because `venv/` only matches directories, not files (symlinks).

Expand All @@ -80,7 +82,7 @@ To ignore both directories and symlinks, remove the trailing slash:
+ tests/venv
```

## Local git ignores are not shared
## Local git ignores are not shared (git-specific)

The local git ignore file, `.git/info/exclude`, is specific to the main worktree's git directory and is not respected in other worktrees. Personal ignore patterns for your editor or temporary files may not apply in new worktrees, causing them to appear in `git status`.

Expand Down
8 changes: 5 additions & 3 deletions docs/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A workflow tool for managing git worktrees and tmux windows as isol

# What is workmux?

workmux is a giga opinionated zero-friction workflow tool for managing [git worktrees](https://git-scm.com/docs/git-worktree) and tmux windows as isolated development environments. Also supports [kitty](/guide/kitty), [WezTerm](/guide/wezterm), and [Zellij](/guide/zellij) (experimental). Perfect for running multiple AI agents in parallel without conflict.
workmux is a giga opinionated zero-friction workflow tool for managing [git worktrees](https://git-scm.com/docs/git-worktree) (or [jj workspaces](https://jj-vcs.github.io/jj/latest/working-copy/#workspaces)) and tmux windows as isolated development environments. Also supports [kitty](/guide/kitty), [WezTerm](/guide/wezterm), and [Zellij](/guide/zellij) (experimental). Perfect for running multiple AI agents in parallel without conflict.

**Philosophy:** Do one thing well, then compose. Your terminal handles windowing and layout, git handles branches and worktrees, your agent executes, and workmux ties it all together.

Expand Down Expand Up @@ -122,7 +122,7 @@ state, editor session, dev server, and AI agent. Context switching is switching

## Features

- Create git worktrees with matching tmux windows (or kitty/WezTerm/Zellij tabs) in a single command (`add`)
- Create worktrees (git) or workspaces (jj) with matching tmux windows (or kitty/WezTerm/Zellij tabs) in a single command (`add`)
- Merge branches and clean up everything (worktree, tmux window, branches) in one command (`merge`)
- [Dashboard](/guide/dashboard/) for monitoring agents, reviewing changes, and sending commands
- [Delegate tasks to worktree agents](/guide/skills#-worktree) with a `/worktree` skill
Expand Down Expand Up @@ -186,9 +186,11 @@ workmux merge

In a standard Git setup, switching branches disrupts your flow by requiring a clean working tree. Worktrees remove this friction. `workmux` automates the entire process and pairs each worktree with a dedicated tmux window, creating fully isolated development environments.

**Using jj?** workmux also supports [jj (Jujutsu)](https://jj-vcs.github.io/jj/) natively. jj workspaces provide the same parallel development benefits. workmux auto-detects your VCS backend.

## Requirements

- Git 2.5+ (for worktree support)
- Git 2.5+ (for worktree support) or [jj](https://jj-vcs.github.io/jj/) (Jujutsu)
- tmux (or [WezTerm](/guide/wezterm), [kitty](/guide/kitty), or [Zellij](/guide/zellij))

## Inspiration and related tools
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ workmux add new-feature

This will:

- Create a git worktree at `<project_root>/../<project_name>__worktrees/new-feature`
- Create a worktree (git) or workspace (jj) at `<project_root>/../<project_name>__worktrees/new-feature`
- Copy config files and symlink dependencies (if [configured](/guide/configuration#file-operations))
- Run any [`post_create`](/guide/configuration#lifecycle-hooks) setup commands
- Create a tmux window named `wm-new-feature` (the prefix is configurable)
Expand Down
5 changes: 4 additions & 1 deletion docs/guide/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ git push -u origin feature-123
gh pr create
```

> For jj users, push with `jj git push` instead.


Once your PR is merged on GitHub, use `workmux remove` to clean up:

```bash
Expand All @@ -127,4 +130,4 @@ workmux remove feature-123
workmux rm --gone
```

The `--gone` flag is particularly useful - it automatically finds worktrees whose upstream branches no longer exist (because the PR was merged and the branch was deleted on GitHub) and removes them.
The `--gone` flag is particularly useful - it automatically finds worktrees whose upstream branches no longer exist (because the PR was merged and the branch was deleted on GitHub) and removes them. For jj repos, workmux detects gone branches via `jj git fetch` and bookmark tracking.
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
layout: home
description: The zero-friction workflow for git worktrees and tmux, kitty, WezTerm, or Zellij
description: The zero-friction workflow for git worktrees, jj workspaces, and tmux, kitty, WezTerm, or Zellij
---

<div class="mono-editorial">
Expand Down Expand Up @@ -61,7 +61,7 @@ description: The zero-friction workflow for git worktrees and tmux, kitty, WezTe
<div class="ed-container">
<div class="ed-accent-rule"></div>
<span class="ed-section-label">Worktree pain points, solved</span>
<p class="ed-section-desc">Git worktrees are powerful, but managing them manually is painful. workmux automates the rough edges.</p>
<p class="ed-section-desc">Git worktrees (and jj workspaces) are powerful, but managing them manually is painful. workmux automates the rough edges.</p>
<div class="ed-pain-points-list">
<div class="ed-pain-point">
<h3>"You need to reinstall everything"</h3>
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/commands/add.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
description: Create git worktrees and tmux windows, with support for AI prompts and parallel generation
description: Create worktrees and tmux windows, with support for AI prompts and parallel generation
---

# add

Creates a new git worktree with a matching tmux window and switches you to it immediately. If the branch doesn't exist, it will be created automatically.
Creates a new worktree with a matching tmux window and switches you to it immediately. If the branch doesn't exist, it will be created automatically.

```bash
workmux add <branch-name> [flags]
Expand Down Expand Up @@ -48,7 +48,7 @@ These options allow you to skip expensive setup steps when they're not needed (e
## What happens

1. Determines the **handle** for the worktree by slugifying the branch name (e.g., `feature/auth` becomes `feature-auth`). This can be overridden with the `--name` flag.
2. Creates a git worktree at `<worktree_dir>/<handle>` (the `worktree_dir` is configurable and defaults to a sibling directory of your project)
2. Creates a worktree (via `git worktree add` or `jj workspace add`) at `<worktree_dir>/<handle>` (the `worktree_dir` is configurable and defaults to a sibling directory of your project)
3. Runs any configured file operations (copy/symlink)
4. Executes `post_create` commands if defined (runs before the tmux window/session opens, so keep them fast)
5. Creates a new tmux window named `<window_prefix><handle>` (e.g., `wm-feature-auth` with `window_prefix: wm-`). With `--session`, the window is created in its own dedicated session instead of the current session.
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/commands/list.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
description: List all git worktrees with their agent, window, and merge status
description: List all worktrees with their agent, window, and merge status
---

# list

Lists all git worktrees with their agent status, multiplexer window status, and merge status. Alias: `ls`
Lists all worktrees with their agent status, multiplexer window status, and merge status. Alias: `ls`

```bash
workmux list [options] [worktree-or-branch...]
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/commands/merge.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ If your workflow uses pull requests, the merge happens on the remote after revie

By default, `workmux merge` performs a standard merge commit (configurable via `merge_strategy`). You can override the configured behavior with these mutually exclusive flags:

- `--rebase`: Rebase the feature branch onto the target before merging (creates a linear history via fast-forward merge). If conflicts occur, you'll need to resolve them manually in the worktree and run `git rebase --continue`.
- `--squash`: Squash all commits from the feature branch into a single commit on the target. You'll be prompted to provide a commit message in your editor.
- `--rebase`: Rebase the feature branch onto the target before merging (creates a linear history via fast-forward merge). If conflicts occur, you'll need to resolve them manually in the worktree and run `git rebase --continue`. For jj repos, this uses `jj rebase`.
- `--squash`: Squash all commits from the feature branch into a single commit on the target. You'll be prompted to provide a commit message in your editor. For jj repos, this uses `jj squash`.

If you don't want to have merge commits in your main branch, use the `rebase` merge strategy, which does `--rebase` by default.

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/commands/remove.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ workmux remove [name]... [flags]
| Flag | Description |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--all` | Remove all worktrees at once (except the main worktree). Prompts for confirmation unless `--force` is used. Safely skips worktrees with uncommitted changes or unmerged commits. |
| `--gone` | Remove worktrees whose upstream remote branch has been deleted (e.g., after a PR is merged on GitHub). Automatically runs `git fetch --prune` first. |
| `--gone` | Remove worktrees whose upstream remote branch has been deleted (e.g., after a PR is merged on GitHub). Automatically runs `git fetch --prune` (or `jj git fetch` for jj repos) first. |
| `--force, -f` | Skip confirmation prompt and ignore uncommitted changes. |
| `--keep-branch, -k` | Remove only the worktree and tmux window while keeping the local branch. |

Expand Down
11 changes: 11 additions & 0 deletions skills/merge/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ This command finishes work on the current branch by:
2. Rebasing onto the base branch
3. Running `workmux merge` to merge and clean up

## Step 0: Detect VCS

Determine the VCS backend:
- If `.jj/` directory exists at or above the current directory → jj mode
- Otherwise → git mode

For jj mode, adapt the steps below:
- Step 1: Use `jj describe` instead of `git commit`
- Step 2: Use `jj rebase -d <base>` instead of `git rebase`
- For conflicts: Use `jj resolve` and inspect with `jj diff`

## Step 1: Commit

If there are staged changes, commit them. Use lowercase, imperative mood, no conventional commit prefixes. Skip if nothing is staged.
Expand Down
7 changes: 7 additions & 0 deletions skills/rebase/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ Rebase the current branch.

Arguments: $ARGUMENTS

## Step 0: Detect VCS

If `.jj/` exists at or above the current directory → use jj commands:
- `jj git fetch` instead of `git fetch`
- `jj rebase -d <target>` instead of `git rebase`
- For conflicts: `jj resolve` instead of manual conflict resolution + `git rebase --continue`

Behavior:

- No arguments: rebase on local main
Expand Down
4 changes: 2 additions & 2 deletions skills/worktree/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
name: worktree
description: Launch one or more tasks in new git worktrees using workmux.
description: Launch one or more tasks in new worktrees using workmux.
disable-model-invocation: true
allowed-tools: Bash, Write
---

Launch one or more tasks in new git worktrees using workmux.
Launch one or more tasks in new worktrees using workmux.

Tasks: $ARGUMENTS

Expand Down
39 changes: 21 additions & 18 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::command::args::{MultiArgs, PromptArgs, RescueArgs, SetupFlags};
use crate::{claude, command, config, git, nerdfont};
use crate::{claude, command, config, nerdfont, vcs};
use anyhow::{Context, Result};
use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::{Shell, generate};
Expand All @@ -13,18 +13,19 @@ impl WorktreeBranchParser {
}

fn get_branches(&self) -> Vec<String> {
// Don't attempt completions if not in a git repo.
if !git::is_git_repo().unwrap_or(false) {
return Vec::new();
}
// Don't attempt completions if not in a VCS repo.
let vcs = match vcs::try_detect_vcs() {
Some(v) => v,
None => return Vec::new(),
};

let worktrees = match git::list_worktrees() {
let worktrees = match vcs.list_workspaces() {
Ok(wt) => wt,
// Fail silently on completion; don't disrupt the user's shell.
Err(_) => return Vec::new(),
};

let main_branch = git::get_default_branch().ok();
let main_branch = vcs.get_default_branch().ok();

worktrees
.into_iter()
Expand Down Expand Up @@ -70,18 +71,19 @@ impl WorktreeHandleParser {
}

fn get_handles() -> Vec<String> {
// Don't attempt completions if not in a git repo.
if !git::is_git_repo().unwrap_or(false) {
return Vec::new();
}
// Don't attempt completions if not in a VCS repo.
let vcs = match vcs::try_detect_vcs() {
Some(v) => v,
None => return Vec::new(),
};

let worktrees = match git::list_worktrees() {
let worktrees = match vcs.list_workspaces() {
Ok(wt) => wt,
// Fail silently on completion; don't disrupt the user's shell.
Err(_) => return Vec::new(),
};

let main_worktree_root = git::get_main_worktree_root().ok();
let main_worktree_root = vcs.get_main_workspace_root().ok();

worktrees
.into_iter()
Expand Down Expand Up @@ -130,13 +132,14 @@ impl GitBranchParser {
}

fn get_branches() -> Vec<String> {
// Don't attempt completions if not in a git repo.
if !git::is_git_repo().unwrap_or(false) {
return Vec::new();
}
// Don't attempt completions if not in a VCS repo.
let vcs = match vcs::try_detect_vcs() {
Some(v) => v,
None => return Vec::new(),
};

// Fail silently on completion; don't disrupt the user's shell.
git::list_checkout_branches().unwrap_or_default()
vcs.list_checkout_branches().unwrap_or_default()
}
}

Expand Down
Loading