Configuration
This content is for v0.28. Switch to the latest version for up-to-date documentation.
All wtm files live under <git-common-dir>/wtm/ (.git/wtm/ for a normal clone). Git
never commits anything inside .git/, so wtm is invisible to teammates and to
git status: worktree usage stays personal.
<git-common-dir>/wtm/├── config.toml # project settings├── run.toml # dev jobs + profiles├── schemas/ # JSON schemas for editor autocomplete├── worktrees/<encoded-branch>/│ └── meta.json # source branch, timestamp, env strategy, ordinal, isolation, namespaces├── logs/<encoded-branch>/ # each job's output, one file per job├── hooks/ # the raw output of the last on_create / on_clean run, per branch└── pending-removals.toml # namespace drops a clean still owesEach file and each meta.json field is described in Where wtm keeps its state.
Everything is plain TOML, validated at load time (unknown keys are rejected, not
silently ignored). Edit by hand, or use wtm config show / wtm config edit and the
wtm run import flow.
Project config: config.toml
Section titled “Project config: config.toml”Generated by wtm init, per-clone, never committed.
[worktrees]base_path = "../.trees" # where worktrees are created (relative to repo root)base_branch = "main" # default base for new worktrees
[env]strategy = "example" # example | main | parent (see below)
# Each detected value file and its committed template (schema). wtm distinguishes# templates (.env.example / .dist / .sample / .template / .tmpl, committed) from# value files (.env, gitignored). .env.local is detected and flagged local but# stays syncable.[[env.file]]target = ".env"template = ".env.example"
[[env.file]]target = ".env.local"local = true
[hooks]on_create = [ "pnpm install", # string: runs from worktree root { cmd = "pnpm install", cwd = "apps/api" }, # object: runs from a subdir { cmd = "pnpm install", cwd = "apps/web", continue_on_error = true }, # non-fatal]on_clean = [ "docker compose down", # runs right before a worktree is removed]on_create hooks run after a worktree is created; on_clean hooks run in the worktree
just before it is removed by clean/prune (e.g. to tear down external resources). A
non-zero hook aborts the operation unless the entry sets continue_on_error. Hooks
interpolate {{worktree}}, {{branch}}, {{root}}, and (for on_create) {{from_branch}}. A hook
is a /bin/sh line and each value is quoted for the spot it lands in, so a path holding a
' or a $ reaches the command as it is spelled on disk: cd {{worktree}}, cd "{{worktree}}"
and cd '{{worktree}}' all work.
A hook also gets the worktree's run variables (COMPOSE_PROJECT_NAME, WTM_* and the
declared ports, see Run config) only when run.toml declares
a job running docker compose and the worktree recorded its isolation (isolation in its
meta.json: every worktree created since the choice exists, or adopted with
wtm env <branch> --isolation isolated). Otherwise a hook runs with the environment it
always had, and resolving nothing allocates no ordinal. When the worktree's own .env (in
the directory a compose job runs from) sets COMPOSE_PROJECT_NAME, that value is the one a
hook gets.
Env strategies
Section titled “Env strategies”| Strategy | Behavior |
|---|---|
example |
Copies file.example from the main checkout, renamed to file. Warns if .example is missing. |
main |
Copies the actual file from the main checkout. |
parent |
Copies from the source worktree (--from), falling back to main. |
The strategy is recorded per worktree. Later, wtm env reconciles a
worktree's .env against its template (the committed schema) plus the same value
source, adding missing keys and (with --mode refresh) settling values that drifted.
Override the source for a single run with --from; the report always shows which source
was used.
Global config
Section titled “Global config”Created by wtm init, personal to each developer. It lives under the OS config
directory (~/.config/wtm/config.toml on Linux, ~/Library/Application Support/wtm/config.toml
on macOS), and wtm run proxy status prints the resolved path.
shell = "zsh" # zsh | bash | fish
[ui]animations = true # false disables every wtm ui animation (tab rule, new-row flash)
[proxy]port = 11080 # where named job URLs are served, on the loopback onlyenabled = true # false sends every URL back to http://localhost:<port>ui.animations defaults to on when absent; set it to false to turn off every
wtm ui animation at once, useful over a slow or laggy connection.
[proxy] serves each worktree's HTTP jobs under their own hostname
(http://<job>.<worktree>.<repo>.localhost:11080), so two worktrees stop sharing one
cookie jar. A job opts in with url = { port = "PORT" } in run.toml, which wtm run init writes for the services it detects; url.host replaces the job's name as the first
label (url = { port = "PORT", host = "api" }). Both keys default
to the values above; the proxy lives in the background daemon and dies with it, and a port
it cannot bind costs the names, never the jobs.
A job started by another job is served too. A root turbo run dev is one process
holding several apps, declared as runs = ["web", "api"]: starting it registers the name
of every published job it runs, so http://web.<worktree>.<repo>.localhost:11080 answers
even though the only job the daemon holds is the runner. Those addresses are reported on
the runner (under its line in a run, on its row in wtm ui), and the apps it holds get
no row of their own while it is up: they are its subprocesses, not jobs beside it.
IDE autocomplete + validation
Section titled “IDE autocomplete + validation”Every TOML file wtm init writes starts with a #:schema ./schemas/...json directive.
Pair it with Even Better TOML
(or the bundled JetBrains TOML plugin) for autocomplete, hover docs, and real-time
validation. Schemas ship with the binary and are written beside each file every time wtm
writes it (init, init --only, run init, run job / run profile, relocate…), no
internet required. To refresh them after upgrading without touching a config:
wtm schema dump # <git-common-dir>/wtm/schemas/{run,project}.schema.jsonwtm schema dump --global # global.schema.json, beside the global config