swm organizes your code into per-story git worktrees and launches isolated terminal-multiplexer sessions around them. Each unit of work (a story) gets its own branch, its own worktrees across every affected repository, and its own tmux socket — so switching between tasks is instant and context-free.
- Work on multiple features simultaneously without stashing, without branch juggling, and without one project's environment polluting another.
- Clone any repository to its canonical path (
~/code/repositories/<host>/<org>/<repo>) with a single command and have it immediately available to all stories. - Open a story's workspace from anywhere and land exactly where you left off.
- Automate pre/post actions (branch protection, issue transitions, Slack notifications) with the hook system.
swm is a plugin-host CLI. The host owns the filesystem layout, the story store, plugin lifecycle, and the CLI surface. Every integration is an external plugin binary connected over gRPC.
swm (host)
├── session plugin — terminal multiplexer (bundled: tmux)
├── vcs plugin — version control (bundled: git)
├── forge plugins — code-hosting platforms (bundled: github)
├── picker plugin — interactive selection UI (bundled: fzf)
└── hooks — plain executables, not gRPC
Five capability surfaces:
| Capability | What it does | Bundled plugin |
|---|---|---|
session |
Opens/closes workspaces and pane groups | session-tmux |
vcs |
Clones repos, creates/removes worktrees | vcs-git |
forge |
Lists and creates pull requests | forge-github |
picker |
Interactive selection prompts | picker-fzf |
hook |
Lifecycle event scripts (plain executables) | — |
Filesystem layout (defaults):
~/code/
├── repositories/ # canonical clones, one per remote
│ └── github.com/org/repo/
└── stories/ # worktrees, one per story per repo
└── <story>/github.com/org/repo/
go install github.com/kalbasit/swm/cmd/swm@latestThe bundled plugins are separate binaries. Build them from the repo:
git clone https://github.com/kalbasit/swm
cd swm
mkdir -p ~/.local/bin
go build -o ~/.local/bin/swm-plugin-session-tmux ./plugins/session-tmux
go build -o ~/.local/bin/swm-plugin-vcs-git ./plugins/vcs-git
go build -o ~/.local/bin/swm-plugin-forge-github ./plugins/forge-github
go build -o ~/.local/bin/swm-plugin-picker-fzf ./plugins/picker-fzfnix profile install github:kalbasit/swm#swm-fullswm-full includes the host and all bundled plugins.
Create $XDG_CONFIG_HOME/swm/config.toml (defaults to ~/.config/swm/config.toml):
code_root = "~/code"
default_story = "_default"
[plugins]
session = "tmux"
vcs = "git"
picker = "fzf"
forges = ["github"]
# forge-github authenticates via `gh auth token` by default (no config needed
# if you have the GitHub CLI installed and logged in). Set token_path only to
# override with an explicit file.
# [plugins.config.forge-github]
# token_path = "~/.config/swm/github_token"See cmd/swm/README.md for the full configuration reference.
To customise the tmux window/pane layout when a workspace is opened, create
$XDG_CONFIG_HOME/swm/session-tmux.toml (or a per-repo .swm/session-tmux.toml).
See the session-tmux layout documentation for the full schema and examples.
Once installed and configured, here is a complete first-use walkthrough.
1. Clone a repository
swm clone https://github.com/org/repo
# cloned to ~/code/repositories/github.com/org/repoThe repo lands at its canonical path under code_root/repositories/. You only need to clone once — every story shares the same clone.
2. Create a story
swm story create my-feature
# created story "my-feature" with branch "feat/my-feature"This writes a story record ($XDG_DATA_HOME/swm/stories/my-feature.json) with an empty project list and branch feat/my-feature. Override the branch with --branch:
swm story create my-feature --branch fix/my-featureThe default feat/<name> pattern is configurable via branch_name_template in config.toml — see the full configuration reference.
3. Open the workspace
swm workspace open my-featureWhat happens depends on whether a picker plugin (fzf) is configured:
-
With picker: An interactive list of all repos under
code_root/repositories/is shown. Select the project you want to work on. swm creates a worktree for that project on the story's branch and opens a tmux socket dedicated to this story, with a tmux session for the selected project. -
Without picker: swm opens all projects already attached to the story (none on a fresh story, so this is only useful after a previous picker-based open or manual attachment).
After the first open, the selected project is attached to the story permanently. Next time you open the workspace, you can pick additional projects or re-attach to the same one.
4. Return to the workspace later
Run the same command from any terminal to re-attach:
swm workspace open my-featureYou can also omit the story name if you export SWM_STORY in your shell profile:
export SWM_STORY=my-feature
swm workspace open5. Clean up
swm story remove my-feature # prompts for confirmation
swm story remove my-feature --force # skips confirmationThis removes all worktrees attached to the story and deletes the story record. The canonical clone under repositories/ is left untouched.
Plugins are resolved in this order (first match per capability wins):
- Explicit path in
config.tomlunder[plugins.paths] $XDG_DATA_HOME/swm/plugins/<name>/swm-plugin-<capability>-<name>swm-plugin-<capability>-<name>on$PATH
Hooks are plain executables placed in tiered directories. All applicable tiers run for each event — they compose rather than override. Each hook runs with its working directory set to a contextually appropriate path (the worktree, the repo root, or code_root depending on the event).
| Tier | Path |
|---|---|
| Global | $XDG_CONFIG_HOME/swm/hooks/<event>.d/* |
| Per-repository | <repo>/.swm/hooks/<event>.d/* |
| Per-story | $XDG_CONFIG_HOME/swm/stories/<story>/hooks/<event>.d/* |
pre-* hooks abort the operation on non-zero exit. post-* hooks log failures but do not abort.
See the Hook System reference in the host CLI README for the full event list, working directories, environment variables, and stdin JSON contract.
| Module | Description |
|---|---|
cmd/swm |
Host CLI — commands, config, plugins, hooks |
sdk/go |
Go SDK for writing plugins |
plugins/session-tmux |
tmux session plugin |
plugins/vcs-git |
git VCS plugin |
plugins/forge-github |
GitHub forge plugin |
plugins/picker-fzf |
fzf picker plugin |
- Fork the repo and create a feature branch.
- All changes require tests (TDD). Run
task testbefore pushing. - Format and lint:
task fmt && task lint. - Commit messages follow Conventional Commits.
- Open a pull request against
main.
For larger changes, open an issue first to discuss the approach.
Apache 2.0 — see LICENSE.