devtools is a small Go CLI for managing development checkouts as Git worktrees and pairing each worktree with a tmux session.
By default it uses ~/dev as the workspace root. Multiple roots can be configured; projects are discovered across all of them. Roots come from, in order of precedence:
--root PATH, repeatable:devtools --root ~/dev --root ~/work listDEVTOOLS_ROOT, accepting a colon-separated list of pathsrootlines in the config file (see Configuration)~/dev
The first root is the primary root: new and clone create projects there. All other commands search every root.
- Go 1.26 or newer
- Git
- tmux
- fzf, for interactive picking with
devtools pick,devtools sessions, or ambiguousdevtools switchqueries - Nix, optional, for the provided dev shell and package build
Run from this repository:
go install ./cmd/devtoolsOr build with Nix:
nix builddevtools [--root PATH]... <command> [args]Commands:
devtools new <project-name>
devtools clone <repo-url> [project-name]
devtools migrate [--allow-dirty] [path]
devtools work <branch> [--from <start-point>]
devtools merge
devtools update [--all]
devtools push
devtools rebase [--onto <base>]
devtools done [worktree] [--force] [--keep-branch] [--allow-main]
devtools list
devtools status [--all]
devtools switch [path-or-query]
devtools pick
devtools sessions
devtools bookmark add <name> [path]
devtools bookmark remove <name>
devtools bookmark listCommon flow:
devtools clone git@github.com:owner/repo.git repo
cd ~/dev/repo/main
devtools work feature/example
devtools mergeclone creates a bare repository at:
<root>/<project>/.bare
and creates the default branch as a worktree at:
<root>/<project>/<branch>
Feature branches are converted to filesystem-safe worktree names, so feature/example becomes feature-example.
Creates a normal Git repository on the main branch and switches to its tmux session.
devtools new scratchClones a repository into the bare-worktree layout, creates the remote default branch worktree, and switches to its tmux session.
devtools clone git@github.com:owner/repo.git
devtools clone git@github.com:owner/repo.git custom-nameMigrates an existing checkout into the managed layout. Checkouts with tracked uncommitted changes are rejected unless --allow-dirty is passed. Untracked files are ignored by this check.
devtools migrate
devtools migrate ~/src/repo --allow-dirtyCreates a new worktree for a branch in the current managed project and switches to its tmux session.
devtools work feature/api
devtools work experiment --from origin/mainWhen --from is omitted, the current checkout's HEAD is used as the start point.
Merges the current worktree branch into the main worktree, fast-forwarding when Git can, then removes the merged worktree and deletes its local branch. Both worktrees must be clean. Pass --squash to squash the branch changes into one new commit on main; Git opens your editor for the commit message.
devtools merge
devtools merge --squashFetches from origin and fast-forwards the current project's main or master worktree. The main worktree must be clean. Pass --all to update main worktrees for all managed projects under all workspace roots.
devtools update
devtools update --allPushes the current branch. If the branch has no upstream, it pushes to origin and sets the upstream.
devtools pushRebases the current worktree onto the current project's main or master branch by default. The worktree must be clean. Use --onto to choose a different base ref.
devtools rebase
devtools rebase --onto origin/mainRemoves a worktree and, by default, deletes its local branch. The worktree must be clean and pushed unless --force is used.
devtools done
devtools done feature-api --keep-branch
devtools done feature-api --forceRemoving main or master requires --allow-main.
Lists discovered worktrees under all workspace roots, plus any bookmarks.
devtools listShows a compact dashboard with branch, clean or dirty state, ahead/behind counts, and upstream. Untracked files are ignored when computing the dirty state.
When run from inside a managed worktree, only worktrees for that project are shown. From outside a managed project, all workspace roots are shown. Pass --all to show all workspace roots from anywhere. Bookmarks are not included.
devtools status
devtools status --allSwitches to a tmux session for a worktree. The target can be an absolute path or a query matched against discovered labels.
devtools switch repo/main
devtools switch feature-apiIf no target is provided, or if a query matches multiple worktrees, fzf is used to pick one.
Always opens the interactive picker and switches to the selected worktree.
devtools pickOpens an interactive picker for active tmux sessions and switches to the selected session.
devtools sessionsManages named bookmarks for directories that do not follow the managed bare-worktree layout, like a dotfiles checkout or a notes directory. Bookmarks show up in list, switch, and pick alongside discovered worktrees, and their tmux session is named after the bookmark.
add stores the given path, or the current directory when the path is omitted. Bookmarks are stored in the config file.
devtools bookmark add dotfiles ~/.dotfiles
devtools bookmark add notes
devtools bookmark list
devtools bookmark remove notes
devtools switch dotfilesdevtools reads an optional config file from $DEVTOOLS_CONFIG, falling back to $XDG_CONFIG_HOME/devtools/config and then ~/.config/devtools/config. The format is line-based; blank lines and lines starting with # are ignored:
# workspace roots, searched in order; the first is the primary root
root ~/dev
root ~/work
# name followed by path; paths may contain spaces
bookmark dotfiles ~/.dotfiles
bookmark notes ~/Documents/notes
root lines are used only when neither --root nor DEVTOOLS_ROOT is set. bookmark lines are managed by devtools bookmark add and devtools bookmark remove, but can also be edited by hand.
Enter the Nix dev shell:
nix developRun tests:
go test ./...Run the formatter for the Nix files:
nix fmtBuild the Nix package:
nix build