G
YouTube TikTok Games
Skip to content

Configuration

Copy page

workmux uses a two-level configuration system:

  • Global (~/.config/workmux/config.yaml): Personal defaults for all projects. Run workmux config edit to open it in your editor.
  • Project (.workmux.yaml): Project-specific overrides

Project settings override global settings. When you run workmux from a subdirectory, it walks upward to find the nearest .workmux.yaml, allowing nested configs for monorepos. See Monorepos for details. For hook lists (post_create, pre_merge, pre_remove) and file operation lists (files.copy, files.symlink, remote_files.copy, remote_files.symlink), you can use "<global>" to include global values alongside project-specific ones. Other settings like panes are replaced entirely when defined in the project config.

workmux respects the XDG Base Directory Specification:

PurposeEnvironment variableDefault
ConfigurationXDG_CONFIG_HOME~/.config
CacheXDG_CACHE_HOME~/.cache
StateXDG_STATE_HOME~/.local/state

All workmux files live under a workmux/ subdirectory within these base directories. If you have an existing config at the default location and later set a custom XDG_CONFIG_HOME, workmux will fall back to reading from ~/.config/workmux/ if no config exists at the new location.

The host-side log lives at $XDG_STATE_HOME/workmux/workmux.log and includes sandbox network-proxy rejections - see Debugging blocked requests.

~/.config/workmux/config.yaml:

nerdfont: true # Enable nerdfont icons (prompted on first run)
merge_strategy: rebase # Make workmux merge do rebase by default
merge_keep: true # Keep worktree, window, and branch after merge by default
agent: claude
panes:
- command: <agent> # Start the configured agent (e.g., claude)
focus: true
- split: horizontal # Second pane with default shell

.workmux.yaml:

post_create:
- "<global>"
- mise use
files:
symlink:
- "<global>" # Include global symlinks (node_modules)
- .pnpm-store # Add project-specific symlink
panes:
- command: pnpm install
focus: true
- command: <agent>
split: horizontal
- command: pnpm run dev
split: vertical

For a real-world example, see workmux’s own .workmux.yaml.

Most options have sensible defaults. You only need to configure what you want to customize.

OptionDescriptionDefault
main_branchBranch to merge intoAuto-detected
base_branchDefault base ref for new worktrees, or auto for the effective main branchCurrent branch
worktree_dirDirectory for worktrees (absolute or relative). Supports ~ and {project}.<project>__worktrees/
nerdfontEnable nerdfont icons (prompted on first run)Prompted
window_prefixOverride tmux window/session prefix. Supports {project}.Icon or wm-
window_placementNew tmux window placement (after_current or rightmost)after_current
agentDefault agent for <agent> placeholderclaude
agentsNamed agent commands (global-only). See named agents.{}
prompt_file_onlyWrite prompt files without injecting into agent commandsfalse
merge_strategyDefault merge strategy (merge, rebase, squash)merge
merge_keepKeep resources after workmux merge by defaultfalse
themeDashboard color scheme (see themes)default (auto dark/light)
modeTmux mode (window or session). See session mode.window
default_sessionSession to return to when a workmux session closes (session mode)Previous session
placementWhere workmux add creates agents: local or a remote name. See remotes.local

Set base_branch: auto to create new branches from the effective main branch, regardless of the currently checked-out branch. Workmux uses configured main_branch, then the local origin/HEAD, main, or master. Detection uses local Git state and does not fetch. An explicit workmux add --base takes precedence. When base_branch is omitted, new branches use the checked-out branch.

In window_prefix and worktree_dir, {project} resolves to the same name from any checkout of a repository. Regular repositories use the main checkout’s directory name. For bare repositories, a dot-prefixed directory uses its parent’s name; other directory names have one trailing .git removed.

Repository{project}Relative worktree_dir base
Regular /repos/my-projectmy-project/repos/my-project
Bare /repos/my-projectmy-project/repos/my-project
Bare /repos/my-project/.baremy-project/repos/my-project/.bare
Bare /repos/my-project/.anythingmy-project/repos/my-project/.anything
Bare /repos/my-project/.gitmy-project/repos/my-project
Bare /repos/my-project.gitmy-project/repos/my-project.git

Git reports a bare directory named .git using its parent as the main worktree anchor. Other bare directories are their own anchors. For example, worktree_dir: "../" places checkouts beside .bare, but alongside my-project for the my-project/.git layout.

Regular checkouts named .my-project or my-project.git keep those names. A bare my-project.git/.bare uses its parent name my-project.git verbatim. Without an explicit worktree_dir, the default directory is a sibling of the anchor, named <anchor-directory-name>__worktrees.

Changing a resolved prefix does not rename existing windows or sessions; close them before changing the prefix and reopen them afterward. Changing a templated worktree directory affects new worktrees, not existing ones.

remotes defines the SSH servers that can run agents. See remote servers for setup and usage.

~/.config/workmux/config.yaml
remotes:
devbox:
host: devbox
path: ~/.workmux-node
# isolation: container # optional: force Docker regardless of sandbox.enabled
placement: devbox
OptionDescriptionDefault
remotes.<name>.hostSSH destination: an alias from ~/.ssh/config or user@hostrequired
remotes.<name>.pathNode root on the remote: ~, a path starting with ~/ (expanded on the remote), or an absolute path~/.workmux-node
remotes.<name>.isolationcontainer forces Docker; none forces host execution. See server or Dockerfollows sandbox.enabled
remotes.<name>.binaryLocal path to a Linux workmux binary to install instead of the matching releasenone

remotes is global-only: in a project’s .workmux.yaml it is ignored with a warning. Unknown keys under a remote are an error.

placement sets where workmux add creates agents when neither --remote nor --local is given. It can be set globally or per project; the project value wins. It can only name a remote from the global remotes, and it is ignored when no remotes are configured.

The dashboard supports 12 color schemes, each with dark and light variants. Dark/light mode is auto-detected from your terminal background.

Press T (shift+t) in the dashboard to cycle through schemes. The selection persists to your global config (~/.config/workmux/config.yaml).

Available schemes: default, emberforge, glacier-signal, obsidian-pop, slate-garden, phosphor-arcade, lasergrid, mossfire, night-sorbet, graphite-code, festival-circuit, teal-drift.

# Just a scheme name (auto-detect dark/light)
theme: emberforge
# Force a specific mode
theme:
scheme: emberforge
mode: light

You can override individual palette colors using the custom block. Custom colors are applied on top of the base scheme, so you can start from any built-in theme and tweak specific colors. Values can be hex colors ("#51afef"), named colors (red, cyan), or terminal color indices (42):

theme:
custom:
bg: "#282c34"
fg: "#bbc2cf"
accent: "#51afef"
success: "#98be65"
warning: "#ECBE7B"
error: "#ff6c6b"

You can also combine custom colors with a specific scheme and mode:

theme:
scheme: emberforge
mode: dark
custom:
accent: "#51afef"
danger: "#ff6c6b"

Shorthand aliases: bg for current_row_bg, fg for text, error for danger.

All palette fields: current_row_bg, highlight_row_bg, current_worktree_fg, dimmed, text, border, help_border, help_muted, header, keycap, info, success, warning, danger, accent.

Custom colors persist when cycling themes with T.

OptionDescriptionDefault
worktree_namingHow to derive names from branchesfull
worktree_prefixPrefix for worktree directories and windowsnone

worktree_naming strategies:

  • full: Use the full branch name (slashes become dashes)
  • basename: Use only the part after the last / (e.g., prj-123/feature → feature)

Define your multiplexer pane layout with the panes array. For multiple windows in session mode, use windows instead (they are mutually exclusive).

panes:
- command: <agent>
name: agent
focus: true
- command: npm run dev
name: dev
split: horizontal
size: 15

Each pane supports:

OptionDescriptionDefault
namePane display name (currently applied by the Zellij backend)---
commandCommand to run (see agent placeholders below)Shell
focusWhether this pane receives focusfalse
zoomZoom pane to fullscreen (implies focus: true)false
splitSplit direction (horizontal, vertical, or Zellij-only stacked)---
sizeAbsolute size in lines/cells50%
percentageSize as percentage (1-100)50%
target0-based pane index to split from; defaults to most recent panemost recent pane

Use target when a new pane should split from an earlier pane instead of the one that was just created. This is useful for layouts with two panes across the top and one full-width pane on the bottom.

panes:
- command: nvim
focus: true
- command: pnpm install && pnpm run dev
split: vertical
size: 15
- command: <agent>
split: horizontal
target: 0

The example places nvim at the top left, the agent to its right, and the dev server across the bottom. target must refer to a pane that already exists in the same layout. For example, pane 2 can target 0 or 1, but not 2 or any later pane.

size is supported by tmux. Kitty and WezTerm do not provide a fixed-cell split operation. percentage is supported by tmux and WezTerm, and by Kitty when using the splits layout.

split: stacked is supported only with the Zellij backend. It creates the pane in a Zellij stack with the previous pane, or with the pane selected by target.

  • <agent>: resolves to the configured agent (from agent config or --agent flag)
  • <agent:name>: resolves to a named agent profile from global config

Named agent profiles can provide structured command, args, env, and type fields, including env values loaded with from_env. See named agents for the full profile schema.

Built-in agents (claude, gemini, agy, codex, copilot, opencode, kiro-cli, vibe, pi, omp, grok) are auto-detected when used as literal commands and receive prompt injection automatically, without needing the <agent> placeholder or a matching agent config:

panes:
- command: "claude --dangerously-skip-permissions"
focus: true
- command: "codex --yolo"
split: vertical

Each agent receives the prompt (via -p/-P/-e) using the correct format for that agent. Auto-detection matches the executable name regardless of flags or path.

Antigravity CLI support covers command detection, prompt injection, and status tracking through lifecycle hooks installed by workmux setup.

Define reusable pane arrangements in the layouts map and select one at add-time with -l/--layout:

layouts:
design:
panes:
- command: <agent>
focus: true
- command: <agent:codex>
split: vertical
review:
panes:
- command: <agent>
Terminal window
workmux add my-feature -l design

When -l is used, the layout’s panes replace the top-level panes for that worktree. All other config (hooks, files, agent, etc.) comes from the top-level as usual. The -l flag cannot be combined with --agent.

When using session mode, you can configure multiple windows per session using the windows array. This is mutually exclusive with the top-level panes config. See multiple windows per session for full details.

mode: session
windows:
- name: editor
panes:
- command: <agent>
focus: true
- split: horizontal
size: 20
- name: tests
panes:
- command: just test --watch

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

files:
copy:
- .env
symlink:
- .next/cache # Share build cache across worktrees

Both copy and symlink accept glob patterns.

Remote workspaces use independent remote_files.copy and remote_files.symlink lists, with no fallback to files. By default, only the Git checkout is transferred. Explicitly select any supplemental files you need remotely:

remote_files:
copy:
- .env
- CLAUDE.local.md

These lists follow the same config-relative path and "<global>" merge rules as files. Remote symlink sources are uploaded once and shared, not synchronized. See Remote supplemental files for cache behavior and disabling inherited transfers.

To re-apply file operations to existing worktrees (e.g., after updating the config), use workmux sync-files.

Run commands at specific points in the worktree lifecycle, such as installing dependencies or running database migrations. All hooks run with the worktree directory as the working directory (or the nested config directory for nested configs) and receive environment variables: WM_HANDLE, WM_WORKTREE_PATH, WM_PROJECT_ROOT, WM_CONFIG_DIR.

hook_shell is an argv list containing the executable followed by its arguments. Workmux appends each hook command as the final argument. It defaults to ["bash", "-c"] for compatibility. To select a specific Bash installation on macOS, set the machine-specific path in your global configuration:

hook_shell: ["/opt/homebrew/bin/bash", "-c"]

A project can override the complete hook_shell argv in .workmux.yaml; when omitted, it inherits the global value. The argv must contain a non-empty executable. Workmux reports the configured executable when it cannot be launched. Shell flags such as error handling or login behavior are applied only when included explicitly in hook_shell or the hook command. Remote workspaces run hooks with bash -c when hook_shell names an absolute path, since that path refers to a local program; see What the server uses from your config.

HookWhen it runsAdditional env vars
post_createAfter worktree creation, before tmux window opens-
pre_mergeBefore merging (aborts on failure)WM_BRANCH_NAME, WM_TARGET_BRANCH
pre_removeBefore worktree removal (aborts on failure)-

WM_PROJECT_ROOT points to the repository’s main worktree, regardless of where the selected config is stored. WM_CONFIG_DIR points to the corresponding config directory in the new worktree, which may differ from WM_WORKTREE_PATH when using nested configs.

Example:

post_create:
- direnv allow
pre_merge:
- just check

In Node.js projects, pre_remove defaults to a fast node_modules cleanup, and defining your own commands replaces that default. Use "<cleanup-node-modules>" to keep it alongside them:

pre_remove:
- cp -r test-results/ "$WM_PROJECT_ROOT/artifacts/$WM_HANDLE/"
- "<cleanup-node-modules>"

Customize the icons shown in tmux window names:

status_icons:
working: "🤖" # Agent is processing
waiting: "💬" # Agent needs input (auto-clears on focus)
done: "✅" # Agent finished (auto-clears on focus)

You can use tmux style codes for colored icons in both the tmux status bar and the dashboard:

status_icons:
done: "#[fg=#a6e3a1]󰄴#[fg=default]"

Supported tmux style attributes: fg=, bg=, default. Colors can be hex (#a6e3a1), named (red, green, etc.), or indexed (colour196).

Set status_format: false to disable automatic tmux format modification.

Configure LLM-based branch name generation for the --auto-name (-A) flag:

auto_name:
command: "claude -p" # Use a custom command instead of the inferred default
model: "gemini-2.5-flash-lite"
background: true
system_prompt: "Generate a kebab-case git branch name."

The command used for branch name generation is resolved in this order:

  1. auto_name.command is set: uses that command as-is
  2. agent is a known agent (claude, gemini, agy, codex, opencode, kiro-cli, vibe, pi, omp): uses the agent’s CLI with a fast/cheap model automatically
  3. Neither: falls back to the llm CLI (requires installation)

To override back to llm when an agent is configured, set auto_name.command: "llm".

OptionDescriptionDefault
commandCommand for branch name generation (overrides agent profile)Agent profile or llm CLI
modelLLM model to use with the llm CLI (ignored when command set)llm’s default
backgroundAlways run in background when using --auto-namefalse
system_promptCustom system prompt for branch name generationBuilt-in prompt

See workmux add --auto-name for usage details.

  • Worktrees are created in <project>__worktrees as a sibling directory to your project by default
  • If no panes configuration is defined, workmux provides opinionated defaults:
    • For projects with a CLAUDE.md file: Opens the configured agent (see agent option) in the first pane, defaulting to claude if none is set.
    • For all other projects: Opens your default shell.
    • Both configurations include a second pane split horizontally
  • post_create commands are optional and only run if you configure them

Use the panes configuration to automate environment setup. Unlike post_create hooks which must finish before the tmux window opens, pane commands execute immediately within the new window.

This can be used for:

  • Installing dependencies: Run npm install or cargo build in a focused pane to monitor progress.
  • Starting services: Launch dev servers, database containers, or file watchers automatically.
  • Running agents: Initialize AI agents with specific context.

Since these run in standard tmux panes, you can interact with them (check logs, restart servers) just like a normal terminal session.

panes:
# Pane 1: Install dependencies, then start dev server
- command: pnpm install && pnpm run dev
# Pane 2: AI agent
- command: <agent>
split: horizontal
focus: true