Configuration¶
Use global configuration for machine-wide worktree paths, layouts, agents, registered projects, directory workspaces, and lifecycle policy. A trusted repository can override only the settings that are safe to scope to that repository. Daemon replacement and SSH connection policy are always global. Multi-machine sync is optional and also global-only.
Start with Agent workspaces for a practical layout example or Multi-machine sync before enabling fleet settings. This page defines the complete keys, trust boundaries, and storage locations.
Global config lives at ~/.config/kwt/config.toml, or at
$KWT_HOME/config.toml when KWT_HOME is set. Repository-local overrides live
in .kwt.toml and are trust-gated before use. When KWT_HOME is set, that same
directory also holds registry.json and pull-requests.json, isolating kwt's
persistent state as a unit. Without it, each store follows its documented
platform config-directory behavior.
KWT_HOME is an isolation boundary: kwt never imports registry entries from
the platform config directory automatically. To move an existing installation,
stop other kwt processes and copy its registry.json into KWT_HOME before
running kwt with the new home. Do not combine two registry files.
The global file is the source of truth for worktree naming, tmux layouts, agent commands, repository setup rules, and the known project registry.
[worktree]
basedir = "~/.kwt/worktrees"
auto_mkdir = true
[naming]
template = "{{.FullPath}}/{{.Branch}}"
[naming.sanitize_chars]
"/" = "-"
":" = "-"
[agents]
codex = "codex"
claude = "claude"
roborev = "roborev tui"
[layouts]
# default = "quad" # unset or "none" = blank single-pane session
auto_launch_on_add = true
[daemon]
idle_timeout = "2h" # 0s disables automatic background shutdown
auto_restart = "newer" # newer or never
replacement_grace = "5m" # must be positive
[ssh]
idle_timeout = "1h" # 0s tears down after the final lease is released
[[layouts.presets]]
name = "quad"
arrange = "even-horizontal"
panes = ["agent:codex", "agent:claude", "agent:roborev", ""]
[[layouts.presets]]
name = "stack"
arrange = "even-vertical"
panes = ["agent:codex", "agent:claude", "agent:roborev", ""]
The default places newly created worktrees under ~/.kwt/worktrees. Relative
paths in a trusted repository-local .kwt.toml are resolved from that
repository's root; a repository-local worktree.basedir cannot be empty.
For a detected bare-container repository (.bare/ with a checked-out main/
worktree), generated paths preserve the repository's flat sibling layout
instead: the container is the base and the sanitized branch name is the
directory name. An explicit command-line path still takes precedence.
Repository-local path fields cannot reference environment variables, including
naming.template and naming.sanitize_chars replacements. Generated paths
influenced by repository-local naming are not environment-expanded after
rendering, so a template cannot synthesize a reference. Environment expansion
remains available for paths in the global configuration and for explicit
command-line paths. In a global naming template, expansion applies only to
literal template text, preserving Go template variables inside actions.
Global naming.sanitize_chars replacement values expand before branch
sanitization.
Daemon and SSH lifecycle policy is global-only. Repository-local .kwt.toml
files never control daemon lifetime, replacement, connection persistence, or
authority. daemon.idle_timeout
applies only to the detached background daemon; kwt serve stays active until
it is stopped. With auto_restart = "newer", a newer compatible kwt binary
drains and replaces an older daemon, while an older binary continues using a
newer daemon. auto_restart = "never" disables automatic version replacement.
ssh.idle_timeout keeps an unleased OpenSSH master warm for one hour by
default. Zero requests immediate teardown; negative values are invalid.
Resolved worktree and directory-workspace paths cannot contain #, which tmux
reserves for format expansion. kwt rejects such paths before creating or
registering a workspace.
Pane entries are shell commands. agent:<name> expands through the [agents]
table before tmux starts, so command flags live in one local config file.
layouts.default is optional. When it is unset — or set to the reserved name
none — workspaces launch as a blank single-pane session in the worktree
directory. Repository-local .kwt.toml files may also set
layouts.default = "none" to opt a single project back into blank sessions
when the global config names a preset.
Project registry¶
The dashboard lists worktrees from the configured base directory, the current
launch repository, and registered projects. Running kwt inside a repository
registers or refreshes that repository so future dashboard launches can find its
worktrees from anywhere.
Automation and graphical clients can perform the same explicit registration
without opening the dashboard by running kwt projects add <path> --json.
They can unregister that metadata with kwt projects remove <path> --json;
the command never deletes repository, worktree, or tmux-session data. The
project registry is structured metadata, so kwt config set projects ...
refuses direct scalar writes that would corrupt it. On startup, kwt also
repairs the historical string-encoded empty value projects = '[]' to the
canonical empty array. Other malformed project values still fail validation.
Project entries are discovery metadata, not worktree-creation policy:
[[projects]]
repository = "github.com/kenn-io/kwt"
name = "kwt"
path = "~/code/kwt"
last_touched = "2026-07-04T12:00:00Z"
Project refreshes update these known fields without discarding additional fields written by a newer kwt version.
Directory workspaces¶
Plain directories registered as tmux workspaces, independent of any Git worktree:
Paths are expanded and symlink-resolved on load. Entries are machine-level
configuration: repository-local .kwt.toml files cannot set them, and they are
never published over multi-machine sync. Manage entries with
kwt workspace add|list|remove rather than editing the file; see
Directory workspaces for the workflow.
Repository setup¶
Optional repository settings can copy files or run commands when new worktrees
are created by kwt add:
[[repository_settings]]
repository = "~/code/myapp"
basedir = "./worktrees"
copy_files = ["templates/.env.example"]
setup_commands = [
"npm install",
'printf "branch=%s\npath=%s\n" "{{.Branch}}" "{{.Path}}" > .worktree-info',
]
Template variables include Host, Owner, Repository, FullPath, Branch,
Hash, and Path. Quote variables in shell commands when values may contain
spaces. repository may also be a glob such as **/acme/widget; trusted
repository-local glob selectors remain repository selectors rather than being
resolved as paths beneath that repository.
Remote-only multi-machine sync skips repository setup (copy_files and
setup_commands) because the branch name is reported by another host. Run any
project bootstrap command manually after syncing if that branch needs it.
Multi-machine sync config¶
Multi-machine sync is an opt-in subsystem. The public command namespace is
kwt sync, and the config section is [fleet]. All [fleet] settings must be
set in the global config.toml; values in repository-local .kwt.toml files
are ignored.
[fleet]
enabled = true
host_id = "host-a"
hub_url = "https://host-a.example"
token_file = "~/.config/kwt/fleet.token"
[fleet.hub]
listen_addr = "127.0.0.1:8787"
store_path = "~/.local/share/kwt/fleet/state.json"
Plain http:// hub URLs and hub listeners are accepted only for loopback
hosts. Loopback client requests bypass environment HTTP proxies. Every
multi-machine hub URL must use HTTPS, commonly by serving the loopback hub
through a private TLS endpoint.
See Multi-machine sync for the user-facing workflow and Multi-machine sync architecture for the wire protocol and hub behavior.