WezTerm + Worktrunk + code-agent glue for fast worktree switching from the terminal itself.
The name stays agent-neutral on purpose. The current default implementation targets the Codex CLI, but the repo structure now isolates agent-specific behavior in a single profile file.
- Opens Worktrunk from inside WezTerm with first-class shortcuts.
- Creates predictable worktrees at
.worktrees/<branch>. - Launches or re-attaches a detached code-agent session per repo/branch.
- Keeps the agent alive while still allowing the display to sleep.
- Uses Worktrunk summaries and non-interactive commit generation.
- Adds Worktrunk aliases and hooks for agent resume, manual cache hydration, tmux renaming, and branch-session cleanup.
- Tracks selected repos with conservative status, pull, dirty-work backup, conflict reconciliation, doctor, and optional user-level systemd timers.
- Shows
repo:branchin WezTerm tab titles for worktree tabs.
.wezterm.lua: WezTerm config with Worktrunk actions and tmux-style ergonomics..config/weztrunk/MANUAL.md: built-in manual for day-to-day usage and provider setup..config/weztrunk/config.toml: named provider/profile defaults..config/weztrunk/repos.txt: repos watched byweztrunk repos..config/weztrunk/agent-profile.sh: agent-specific launch and commit behavior..config/weztrunk/providers/: per-provider extra-flag files.shell/weztrunk.sh: sharedbash/zshintegration forwt,wtx, andwtn..config/worktrunk/config.toml: Worktrunk defaults..config/systemd/user/: optional Linux repo-upkeep and backup user services and timers..local/bin/weztrunk: top-level CLI entrypoint for manual and helper subcommands..local/bin/weztrunk-backup: dirty-work snapshot helper..local/bin/weztrunk-config: TOML-backed profile reader..local/bin/weztrunk-doctor: local install and dependency checkup..local/bin/weztrunk-agent: shared agent dispatcher and session utilities..local/bin/weztrunk-manual: manual viewer for shell and WezTerm..local/bin/weztrunk-reconcile: scratch-worktree rebase and conflict-resolution launcher..local/bin/weztrunk-repos: repo status, safe pull, and timer management..local/bin/weztrunk-upkeep: opportunistic pull/backup maintenance without a scheduler..local/bin/wt-code: detached interactive agent launcher..local/bin/worktrunk-code-commit: non-interactive commit-message helper.scripts/install.sh: symlink-based installer with backups.
- macOS or Linux
weztermwt(Worktrunk)dtach- a supported code-agent CLI
gh
Install example:
brew install wezterm worktrunk dtach ghThe default profile expects codex to already be installed and authenticated.
Sleep inhibition is optional and controlled by [sleep] guard in config.toml:
- macOS:
caffeinateis used automatically via/usr/bin/caffeinate -swhen present - Linux:
systemd-inhibit --what=sleepis used automatically when present guard = "none"leaves sleep behavior alone, which is the tracked default for managed-work-machine friendliness
git clone https://github.com/teilomillet/weztrunk.git ~/Code/weztrunk
cd ~/Code/weztrunk
bash scripts/install.sh
source ~/.zshrcOn a managed Mac, or anywhere you want no persistent scheduler links, use:
bash scripts/install.sh --profile managed-macThe installer auto-selects your shell startup file from $SHELL:
zsh->~/.zshrcbash->~/.bashrc- fallback ->
~/.profile
When bash is detected, the installer also checks whether ~/.bash_profile, ~/.bash_login, or ~/.profile appears to source ~/.bashrc. If not, it prints a Linux/login-shell note so you can decide whether to keep ~/.bashrc or target ~/.profile instead.
You can override that with:
WEZTRUNK_SHELL_RC=~/.bash_profile bash scripts/install.shThen reload WezTerm with Cmd+Shift+R on macOS or Ctrl+Shift+R on Linux/Ubuntu.
If you are upgrading an older install, rerun the installer once. This version adds new linked files, so a plain git pull is not enough on an existing machine until those new symlinks exist.
wtx: open the Worktrunk picker, then launch or re-attach the agentwtx feature-x: switch to that worktree and attach the branch sessionwtn feature-x: create the branch/worktree and launch the agentwtx feature-x -- 'Fix flaky test': pass an initial prompt to Codexweztrunk man remove: search the manual for a topicweztrunk profile: show the active provider/profileweztrunk profile list: list the named profilesweztrunk repos status: show branch, dirty state, ahead/behind, and latest commit for watched reposweztrunk repos pull: fetch and fast-forward watched repos only when their worktrees are cleanweztrunk repos timer enable: start the user-level auto-pull timerweztrunk backup snapshot: snapshot dirty watched repos into local stateweztrunk backup timer enable: start the user-level dirty-work backup timerweztrunk upkeep maybe: opportunistic pull/backup, throttled by configweztrunk upkeep status --verbose: show upkeep state, last log, reconcile fingerprints, and reconcile statusweztrunk reconcile status: show watched worktrees and whether they are on top of the base branchweztrunk reconcile current --agent conflict: create a scratch reconciliation worktree for the current repoweztrunk reconcile promote --remove: fast-forward the target branch to a reviewed scratch result, then remove the scratch worktreeweztrunk reconcile adopt --remove: replace the target branch with a reviewed scratch result, with a safety branch firstweztrunk reconcile prune --dry-run: preview old clean scratch worktrees that can be removedweztrunk reconcile watch: keep creating fresh scratch reconciliation worktrees while the current repo changesweztrunk reconcile watch-all: manager loop for every worktree in every watched repoweztrunk doctor: check install links, required commands, SSH/GitHub state, timers, and watched reposwtman merge: same manual search path from the shellwthelporwtm: open the built-in manualwzt: short alias forweztrunk, added only if the name is not already takenwtp: short alias forweztrunk profilewts: short alias forweztrunk repos statuswtpull: short alias forweztrunk repos pullwtd: short alias forweztrunk doctorwtb: short alias forweztrunk backup snapshotwtr: short alias forweztrunk reconcilewt step weztrunk-agent: re-attach the current worktree's agent sessionwt step weztrunk-hydrate: copy gitignored files into the current worktree on demandwt step weztrunk-manual: print the manual from inside a repo
WezTrunk shortcuts on Linux/Ubuntu avoid the Super key because GNOME reserves several Super combinations before WezTerm can receive them.
| Action | macOS | Linux/Ubuntu |
|---|---|---|
| Worktrunk picker + launch/re-attach agent | Cmd+Shift+G |
Ctrl+Shift+G |
| Same action on the leader layer | Cmd+B g |
Ctrl+B g |
| Prompt for branch name, create worktree, launch agent | Cmd+B G |
Ctrl+B G |
| Open the WezTrunk manual in a new tab | Cmd+Shift+M |
Ctrl+Shift+M |
| Prompt for a manual topic and open a focused search in a new tab | Cmd+Shift+/ |
Ctrl+Shift+/ |
| Same manual action on the leader layer | Cmd+B m |
Ctrl+B m |
| Same manual-search action on the leader layer | Cmd+B / |
Ctrl+B / |
| Close the current pane | Cmd+B x |
Ctrl+B x or Ctrl+Alt+X |
| Previous / next tab | Cmd+Shift+[ / Cmd+Shift+] |
Ctrl+Alt+A / Ctrl+Alt+D |
| Jump to tab | Cmd+1 ... Cmd+8, Cmd+9 last tab |
Alt+1 ... Alt+8, Alt+9 last tab |
| Self-documented command-palette entries | Cmd+Shift+P, then type Worktrunk |
Ctrl+Shift+P, then type Worktrunk |
- Each repo/branch gets its own
dtachsocket. - On macOS,
caffeinate -skeeps the process alive without forcing the display on. Note:-sonly prevents system sleep on AC power. - On Linux,
systemd-inhibit --what=sleepis used for the same purpose when available. - Worktrunk summaries and commit generation use the configured provider non-interactively.
- Repo upkeep uses fast-forward-only pulls and skips dirty, detached, untracked, or diverged work.
- Dirty-work backup snapshots diffs and small text untracked files without committing, stashing, or modifying the repo.
- Reconciliation happens in scratch worktrees and never rewrites the active worktree automatically.
post-switchrenames the current tmux window to the active branch when inside tmux.post-removecleans up the detached branch session socket after a worktree is removed.
Watched repos live in repos.txt, one path per line:
~/Code/vauban
~/Code/kayak
~/Code/weztrunk
Useful commands:
weztrunk repos status
weztrunk repos pull
weztrunk repos timer enable
weztrunk repos timer status
weztrunk repos timer disableThe pull command fetches each repo, then fast-forwards only when the current branch is clean and behind its upstream. It skips repos with local changes, detached HEADs, missing upstreams, or diverged branches.
On Linux with systemd user services, weztrunk repos timer enable starts a timer that runs shortly after login and then every five minutes. When notify-send is available, timer runs use desktop notifications for skipped repos. On macOS, the installer skips these systemd unit links and leaves scheduled upkeep to the opportunistic mode below.
For managed Macs or any machine where a persistent login scheduler is undesirable, use opportunistic upkeep instead of launchd/systemd timers. It runs only from normal terminal commands and is throttled by a timestamp under ~/.local/state/weztrunk/upkeep.
weztrunk upkeep maybe
weztrunk upkeep run
weztrunk upkeep status --verbosewtx and wtn call weztrunk upkeep maybe --quiet before switching or creating a worktree. The command checks [upkeep] in config.toml; with mode = "opportunistic", it runs backup snapshot and repos pull only if interval_seconds has elapsed. No daemon, login item, root privileges, launchd job, or systemd timer is required.
By default, opportunistic upkeep also runs one reconcile watch-all --once manager pass. The practical rule is simple: clean worktrees may be fast-forwarded, dirty worktrees are never rewritten, and their current state gets a fresh scratch candidate on top of origin/main.
Backups are local snapshots under ~/.local/state/weztrunk/work-backups by default. For each dirty watched repo, the snapshot includes:
- branch, HEAD, timestamp, and status
- staged and unstaged binary-capable diffs
- untracked file list
- copies of small text untracked files
Useful commands:
weztrunk backup snapshot
weztrunk backup status
weztrunk backup timer enable
weztrunk backup timer status
weztrunk backup timer disableThe timer runs shortly after login and then every ten minutes. Repeated identical dirty states are deduplicated by fingerprint, so a repo does not get a new backup every timer tick unless the work changed.
Use reconcile when a branch or dirty worktree needs to be rebased onto the latest base branch without touching the active worktree.
weztrunk reconcile status
weztrunk reconcile current
weztrunk reconcile current --agent conflict
weztrunk reconcile current --agent always
weztrunk reconcile promote --remove
weztrunk reconcile adopt --remove
weztrunk reconcile prune --dry-run
weztrunk reconcile watch --interval 30 --stable 5
weztrunk reconcile watch-all --once
weztrunk reconcile watch-all --interval 30 --stable 5current creates a branch named like weztrunk/reconcile/<branch>-<timestamp> and a matching scratch worktree under .worktrees/, adding that directory to the repo-local Git exclude if needed. It also writes an ignored .weztrunk/INTENT.md contract inside the scratch worktree so an assistant can see immediately that this is the integration candidate. If the active worktree is dirty, those changes are copied into the scratch worktree and committed as a WIP snapshot there. The scratch branch is then rebased onto origin/HEAD or origin/main.
watch is the manager mode for work that is still moving. It fingerprints the active checkout, waits until it is stable, then runs current from that exact state. If files change while the scratch rebase is being created, it waits and tries again. This keeps an integration worktree on top of origin/main without rewriting the worktree your editor or another agent is using.
watch-all does the same repo-manager work across all worktrees in the watched repos. Clean worktrees are fast-forwarded when Git can do that safely. Dirty, diverged, detached, and scratch worktrees are not rewritten; dirty or diverged worktrees get fresh reconcile candidates instead.
promote moves the target branch to a reviewed scratch result only when Git can fast-forward the target to that scratch branch. It creates a safety branch named like weztrunk/safety/<branch>-before-promote-<timestamp> before moving anything, and --remove deletes the scratch worktree and branch after a successful promotion.
adopt is the explicit reviewed-rewrite path for scratch branches that contain rebased local commits and therefore cannot be promoted by fast-forward. It requires a clean scratch worktree, a clean target worktree, and a scratch result on top of the chosen base. It creates weztrunk/safety/<branch>-before-adopt-<timestamp>, then moves the target branch to the scratch result with reset --hard when the target is checked out or branch -f when it is not.
prune removes old clean reconcile scratch worktrees. By default it keeps the newest two per source branch and only removes candidates at least seven days old; use --dry-run to preview.
The bitter lesson for assistants is part of the model: a reconcile worktree is the integration candidate, not a random side branch. It should be origin/main first, then the user's work above it, with conflicts resolved there so the result can be reviewed and promoted by fast-forward. The active dirty worktree stays as the live editing surface until promotion is intentional.
When the rebase or patch application conflicts, --agent conflict launches the configured code agent in the scratch worktree with instructions to resolve the integration. Review the scratch branch before merging or replacing your original branch.
Run:
weztrunk doctorIt checks required commands, installed symlinks, GitHub CLI auth, SSH config, repo timers, backup timers, and watched repo status. On macOS, doctor treats systemd timers as intentionally skipped.
The default config lives in config.toml and ships with built-in support for:
codexclaude-codeopencode
The moving pieces are:
wt-codeowns session lifecycle:dtach, sleep inhibition, and branch-scoped sockets.weztrunk-agentloads the active agent profile and exposes shared utilities.weztrunk-configreads the shared TOML config and resolves named profiles.agent-profile.shdefinesweztrunk_agent_launch()andweztrunk_agent_commit().providers/contains optional machine-local extra arguments, one CLI argument per line.
Default selection:
[agent]
provider = "codex"
profile = "deep"Quick inspection:
weztrunk profile
weztrunk profile list
weztrunk profile list codexThe normal way to change defaults is to edit ~/.config/weztrunk/config.toml:
[agent]
provider = "claude-code"
profile = "fast"
[sleep]
guard = "none"
[upkeep]
mode = "opportunistic"
interval_seconds = 1800
pull = true
backup = true
reconcile = true
reconcile_agent = "never"If you want machine-local extras after the named TOML profile, edit the matching files under providers/, for example:
For custom providers, add ~/.config/weztrunk/agent-profile.local.sh and define weztrunk_custom_agent_launch() / weztrunk_custom_agent_commit(). The built-in manual covers that flow.
Environment overrides are still accepted for temporary one-off sessions:
WEZTRUNK_AGENT: provider nameWEZTRUNK_PROFILE: profile name inside that providerWEZTRUNK_AGENT_BIN: explicit agent binary pathWEZTRUNK_CONFIG_PATH: alternate config TOML pathWEZTRUNK_AGENT_ARGS_DIR: alternate provider-args rootWEZTRUNK_AGENT_LOCAL_PROFILE: alternate local override fileWEZTRUNK_AGENT_DISPATCHER: alternate dispatcher pathWEZTRUNK_SLEEP_GUARD:auto,none,caffeinate, orsystemd-inhibit
The older WWC_AGENT_BIN name is still accepted for compatibility.
The repo is named weztrunk, not wezterm-worktrunk-codex, because the WezTerm + Worktrunk integration is the stable part. The launched code agent is the replaceable part.