diff --git a/CLAUDE.md b/CLAUDE.md
index 47f08b194a..4140d46ed4 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -43,13 +43,13 @@ Load relevant skills before starting; reload when scope changes mid-session. Pro
## Documentation
-Behavior changes require doc updates. `src/cli/mod.rs` (`after_long_help` plus clap attributes) is the PRIMARY SOURCE for command pages; never hand-edit the generated mirrors under `docs/content/` or `skills/worktrunk/reference/`. Ask: "does `--help` still describe what the code does?" After any doc change run `cargo test --test integration test_docs_are_in_sync`; editing help text (`after_long_help`, `about`, arg docs) also changes the rendered `--help` snapshots, which that test leaves untouched — regenerate them with `cargo insta test --accept -- --test integration "test_help"` (the pre-merge hook runs both). Sync taxonomy, help-text authoring (three render contexts, link text, config-TOML blocks): `docs/CLAUDE.md`.
+Behavior changes require doc updates. `src/cli/mod.rs` (`after_long_help` plus clap attributes) is the PRIMARY SOURCE for command pages; never hand-edit the generated mirrors under `docs/content/`, `skills/worktrunk/reference/`, or `plugins/worktrunk/skills/`. Ask: "does `--help` still describe what the code does?" After any doc change run `cargo test --test integration test_docs_are_in_sync`; editing help text (`after_long_help`, `about`, arg docs) also changes the rendered `--help` snapshots, which that test leaves untouched — regenerate them with `cargo insta test --accept -- --test integration "test_help"` (the pre-merge hook runs both). Sync taxonomy, help-text authoring (three render contexts, link text, config-TOML blocks): `docs/CLAUDE.md`.
Design proposals live in `design/` and are review-only: open a PR to discuss, then close it without merging. They guide implementation PRs but don't land on `main`.
## Plugin Layout
-Per-tool layout and path resolution (Claude/Codex/Gemini), the convention-only Claude manifest, the Codex inline-hooks rationale, the accepted `wt-switch-create` tradeoff, and `test_plugin_layout_is_consolidated`: `plugins/worktrunk/CLAUDE.md`.
+Per-tool layout and path resolution (Claude/Codex/Gemini), the convention-only Claude manifest, the Codex inline-hooks rationale, the generated plugin-skills mirror, the accepted `wt-switch-create` tradeoff, and `test_plugin_layout_is_consolidated`: `plugins/worktrunk/CLAUDE.md`.
## Data Safety
diff --git a/plugins/worktrunk/.codex-plugin/plugin.json b/plugins/worktrunk/.codex-plugin/plugin.json
index 555820d1ec..300eb4fab5 100644
--- a/plugins/worktrunk/.codex-plugin/plugin.json
+++ b/plugins/worktrunk/.codex-plugin/plugin.json
@@ -44,7 +44,6 @@
]
}
},
- "skills": "./skills/",
"interface": {
"displayName": "Worktrunk",
"shortDescription": "Git worktree workflows for parallel Codex sessions.",
diff --git a/plugins/worktrunk/CLAUDE.md b/plugins/worktrunk/CLAUDE.md
index 41556e803c..18b8ff59ff 100644
--- a/plugins/worktrunk/CLAUDE.md
+++ b/plugins/worktrunk/CLAUDE.md
@@ -14,7 +14,7 @@ worktrunk/ ← repo root = marketplace root
├── .agents/plugins/marketplace.json← Codex pointer (source → ./plugins/worktrunk)
├── gemini-extension.json ← Gemini manifest (extensionPath = repo root)
├── hooks/hooks.json ← Gemini activity hooks (call the wt.sh below)
-├── skills -> (this dir) ← Gemini reads ${extensionPath}/skills = repo-root skills/
+├── skills/ ← real dir; Gemini reads ${extensionPath}/skills directly
└── plugins/worktrunk/ ← plugin root (Claude + Codex resolve source here)
├── .claude-plugin/plugin.json ← Claude manifest (metadata only — NO `hooks`
│ or `skills` keys; components load by
@@ -28,8 +28,10 @@ worktrunk/ ← repo root = marketplace root
│ $CLAUDE_PLUGIN_ROOT, Codex via $PLUGIN_ROOT,
│ Gemini via
│ ${extensionPath}/plugins/worktrunk/hooks/wt.sh
- ├── skills -> ../../skills ← symlink; single-sources skills across all
- │ tools and the docs auto-sync
+ ├── skills/ ← generated real-file mirror of repo-root
+ │ skills/ (test_docs_are_in_sync; never
+ │ hand-edit) — real files because Codex's
+ │ installer drops symlinks, see below
├── CLAUDE.md / README.md
└── (Codex activity hooks live *inline* in .codex-plugin/plugin.json's
`hooks` key — see Known Limitations below)
@@ -53,25 +55,61 @@ Path resolution differs by tool, all verified end-to-end against the real CLIs:
conventional path is dead config that can only mask a mislocated file.
- Skills are auto-discovered by scanning `skills/` for `
/SKILL.md`; a
`skills` manifest array only *adds* directories to that scan, so listing
- the defaults is redundant. The installer dereferences the `skills` symlink
- into a real directory in the install cache.
+ the defaults is redundant.
`$CLAUDE_PLUGIN_ROOT` is the plugin root.
-- **Codex**: `.agents/plugins/marketplace.json` `source` object
- `{ "source": "local", "path": "./plugins/worktrunk" }`. Codex reads
- `plugins/worktrunk/.codex-plugin/plugin.json`. `skills: "./skills/"` resolves
- through the same symlink.
+- **Codex** (codex-cli 0.144.1): `.agents/plugins/marketplace.json` `source`
+ object `{ "source": "local", "path": "./plugins/worktrunk" }`. Codex reads
+ `plugins/worktrunk/.codex-plugin/plugin.json`. Skills load by convention —
+ with no `skills` manifest key, Codex scans `/skills/`; an
+ explicit `"skills": "./skills/"` names the same directory, so the manifest
+ carries no `skills` key. The scanned tree is the real-file mirror ("Plugin
+ skills are a generated mirror" below).
- **Gemini**: `gemini-extension.json` at the repo root; `${extensionPath}` is
the repo root, so `${extensionPath}/skills/` is the repo-root `skills/`
directly and `hooks/hooks.json` (repo root) calls the canonical shim at
`${extensionPath}/plugins/worktrunk/hooks/wt.sh`. No symlink or copy.
-All three tools pick up the whole `skills/` directory (Claude and Codex through
-the symlink, Gemini directly), so a new repo-root skill ships everywhere with
-no manifest change — provided its directory contains a `SKILL.md`
-(`test_plugin_layout_is_consolidated` enforces that; a directory without one is
-silently ignored). Claude-only skills reach the other tools too (accepted
-tradeoff — see Known Limitations below).
+All three tools pick up the whole `skills/` set — Gemini reads the repo-root
+directory, Claude and Codex ship the plugin mirror — so a new repo-root skill
+ships everywhere once `test_docs_are_in_sync` regenerates the mirror, provided
+its directory contains a `SKILL.md` (`test_plugin_layout_is_consolidated`
+enforces that; a directory without one is silently ignored). Claude-only
+skills reach the other tools too (accepted tradeoff — see Known Limitations
+below).
+
+### Plugin skills are a generated mirror
+
+`plugins/worktrunk/skills/` is a real-file mirror of the authored repo-root
+`skills/`, regenerated — symlinks dereferenced, stale files deleted — by the
+`sync_plugin_skills_mirror` stage of `test_docs_are_in_sync`; never hand-edit
+it. Repo-root `skills/` stays the authored home: Gemini reads it directly and
+the docs sync writes into it.
+
+The mirror holds real files because of how the plugin ships, verified
+end-to-end against codex-cli 0.144.1 (scratch marketplaces through
+`codex plugin marketplace add` + `codex plugin add`, skill inventory read with
+`codex debug prompt-input`):
+
+- `codex plugin add` copies the plugin into
+ `$CODEX_HOME/plugins/cache///` with a copier
+ that handles only regular files and directories, silently skipping symlink
+ entries (`copy_dir_recursive` in `codex-rs/core-plugins/src/store.rs`), and
+ sessions load from that cache copy. A symlink anywhere in the tree — a
+ top-level `skills` link or a nested one like `reference/README.md` — ships
+ no content. No manifest value can bridge it: manifest paths must stay within
+ the plugin root (`..` and absolute paths are rejected,
+ `resolve_manifest_path` in `codex-rs/core-plugins/src/manifest.rs`).
+- Codex's convention scan (`default_skill_roots`, the empty-`skills` branch of
+ `plugin_skill_roots` in `codex-rs/core-plugins/src/loader.rs`) reads the
+ mirror like any directory.
+- Claude's installer dereferences symlinks, so a symlinked `skills/` worked
+ for Claude; the mirror serves it identically. A symlink also materializes as
+ a plain text file on Windows checkouts, which shipped no skills from a
+ Windows clone to Claude or Codex.
+
+`test_plugin_layout_is_consolidated` pins the no-symlinks invariant;
+`test_docs_are_in_sync` pins content equality with repo-root `skills/`.
## Known Limitations
@@ -105,4 +143,4 @@ The events (Codex's `HookEventsToml` vocabulary, verified against `codex-rs/conf
### Accepted tradeoff: shared `skills/` exposes `wt-switch-create`
-Codex's `"skills": "./skills/"` and Gemini's `${extensionPath}/skills/` both resolve the entire repo-root `skills/`, including `wt-switch-create`, which depends on Claude session-cwd switching (`EnterWorktree`) that neither provides. Accepted: a tool loading a skill it can't act on is harmless, and a single repo-root `skills/` keeps the `worktrunk` skill single-source across all three tools and the docs sync. Don't add per-tool skills subtrees to exclude it.
+Codex's mirrored `skills/` and Gemini's `${extensionPath}/skills/` both carry the entire skill set, including `wt-switch-create`, which depends on Claude session-cwd switching (`EnterWorktree`) that neither provides. Accepted: a tool loading a skill it can't act on is harmless, and a single authored `skills/` keeps the `worktrunk` skill single-source across all three tools and the docs sync. Don't add per-tool skills subtrees to exclude it.
diff --git a/plugins/worktrunk/skills b/plugins/worktrunk/skills
deleted file mode 120000
index 5dcab58e15..0000000000
--- a/plugins/worktrunk/skills
+++ /dev/null
@@ -1 +0,0 @@
-../../skills
\ No newline at end of file
diff --git a/plugins/worktrunk/skills/worktrunk/SKILL.md b/plugins/worktrunk/skills/worktrunk/SKILL.md
new file mode 100644
index 0000000000..ab68bcf997
--- /dev/null
+++ b/plugins/worktrunk/skills/worktrunk/SKILL.md
@@ -0,0 +1,338 @@
+---
+name: worktrunk
+description: Guidance for Worktrunk (the `wt` CLI) — git worktree management, hooks, and config. Load when editing .config/wt.toml or ~/.config/worktrunk/config.toml; adding, modifying, or debugging hooks (post-merge, post-start, pre-commit, pre-merge, post-switch, etc.); configuring commit message generation or command aliases; or troubleshooting wt behavior. Also answers general worktrunk/wt questions.
+license: MIT OR Apache-2.0
+compatibility: Requires the `wt` CLI (https://worktrunk.dev)
+---
+
+# Worktrunk
+
+Help users work with Worktrunk, a CLI tool for managing git worktrees.
+
+## Available Documentation
+
+Reference files are synced from [worktrunk.dev](https://worktrunk.dev) documentation:
+
+- **reference/config.md**: User and project configuration (LLM, hooks, command defaults)
+- **reference/hook.md**: Hook types, timing, and execution order
+- **reference/switch.md**, **merge.md**, **list.md**, etc.: Command documentation
+- **reference/extending.md**: Aliases, multi-step pipelines, custom subcommands, and template-expansion gotchas (two-pass `{% raw %}` deferral, for-each recipes)
+- **reference/llm-commits.md**: LLM commit message generation
+- **reference/tips-patterns.md**: Practical recipes — aliases, per-branch variables, dev server per worktree, parallel agent patterns
+- **reference/shell-integration.md**: Shell integration debugging
+- **reference/troubleshooting.md**: Troubleshooting for LLM and hooks (Claude-specific)
+
+For command-specific options, run `wt --help`. For configuration, follow the workflows below.
+
+## Two Types of Configuration
+
+Worktrunk uses two separate config files with different scopes and behaviors:
+
+### User Config (`~/.config/worktrunk/config.toml`)
+- **Scope**: Personal preferences for the individual developer
+- **Location**: `~/.config/worktrunk/config.toml` (never checked into git)
+- **Contains**: LLM integration, worktree path templates, command settings, user hooks, approved commands
+- **Permission model**: Always propose changes and get consent before editing
+- **See**: `reference/config.md` for detailed guidance
+
+### Project Config (`.config/wt.toml`)
+- **Scope**: Team-wide automation shared by all developers
+- **Location**: `/.config/wt.toml` (checked into git)
+- **Contains**: Hooks for worktree lifecycle (pre-start, pre-merge, etc.)
+- **Permission model**: Proactive (create directly, changes are reversible via git)
+- **See**: `reference/hook.md` for detailed guidance
+
+## Determining Which Config to Use
+
+When a user asks for configuration help, determine which type based on:
+
+**User config indicators**:
+- "set up LLM" or "configure commit generation"
+- "change where worktrees are created"
+- "customize commit message templates"
+- Affects only their environment
+
+**Project config indicators**:
+- "set up hooks for this project"
+- "automate npm install"
+- "run tests before merge"
+- Affects the entire team
+
+**Both configs may be needed**: For example, setting up commit message generation requires user config, but automating quality checks requires project config.
+
+## Core Workflows
+
+### Setting Up Commit Message Generation (User Config)
+
+Most common request. See `reference/llm-commits.md` for supported tools and exact command syntax.
+
+1. **Detect available tools**
+ ```bash
+ which claude codex llm aichat 2>/dev/null
+ ```
+
+2. **If none installed, recommend Claude Code** (already available in Claude Code sessions)
+
+3. **Propose config change** — Get the exact command from `reference/llm-commits.md`
+ ```toml
+ [commit.generation]
+ command = "..." # see reference/llm-commits.md for tool-specific commands
+ ```
+ Ask: "Should I add this to your config?"
+
+4. **After approval, apply**
+ - Check if config exists: `wt config show`
+ - If not, guide through `wt config create`
+ - Read, modify, write preserving structure
+
+5. **Suggest testing**
+ ```bash
+ wt step commit --show-prompt | head # verify prompt builds
+ wt merge # in a repo with uncommitted changes
+ ```
+
+### Setting Up Project Hooks (Project Config)
+
+Common request for workflow automation. Follow discovery process:
+
+1. **Detect project type**
+ ```bash
+ ls package.json Cargo.toml pyproject.toml
+ ```
+
+2. **Identify available commands**
+ - For npm: Read `package.json` scripts
+ - For Rust: Common cargo commands
+ - For Python: Check pyproject.toml
+
+3. **Design appropriate hooks** (10 hook types: 5 events × pre/post — see `reference/hook.md`)
+ - Dependencies (fast, must complete) → `pre-start`
+ - Tests/linting (must pass) → `pre-commit` or `pre-merge`
+ - Long builds, dev servers → `post-start`
+ - Setup before branch resolution → `pre-switch`
+ - Terminal/IDE updates → `post-switch`
+ - After-commit triggers (CI, notifications) → `post-commit`
+ - Deployment → `post-merge`
+ - Cleanup before removal → `pre-remove`
+ - Cleanup after removal (stop servers, remove containers) → `post-remove`
+
+4. **Validate commands work**
+ ```bash
+ npm run lint # verify exists
+ which cargo # verify tool exists
+ ```
+
+5. **Create `.config/wt.toml`**
+ ```toml
+ # Install dependencies when creating worktrees
+ pre-start = "npm install"
+
+ # Validate code quality before committing
+ [pre-commit]
+ lint = "npm run lint"
+ typecheck = "npm run typecheck"
+
+ # Run tests before merging
+ pre-merge = "npm test"
+ ```
+
+6. **Add comments explaining choices**
+
+7. **Suggest testing**
+ ```bash
+ wt switch --create test-hooks
+ ```
+
+**See `reference/hook.md` for complete details.**
+
+### Adding Hooks to Existing Config
+
+When users want to add automation to an existing project:
+
+1. **Read existing config**: `cat .config/wt.toml`
+
+2. **Determine hook type** - When should this run? (10 types: 5 events × pre/post)
+ - Creating worktree (blocking) → `pre-start`
+ - Creating worktree (background) → `post-start`
+ - Before/after a switch → `pre-switch` / `post-switch`
+ - Before committing → `pre-commit`
+ - After committing (CI, notifications) → `post-commit`
+ - Before merging → `pre-merge`
+ - After merging → `post-merge`
+ - Before/after removal → `pre-remove` / `post-remove`
+
+3. **Handle format conversion if needed**
+
+ Single command to a pipeline of dependent steps:
+ ```toml
+ # Before
+ pre-start = "npm install"
+
+ # After (adding db:migrate, which needs install to finish first)
+ [[pre-start]]
+ install = "npm install"
+
+ [[pre-start]]
+ migrate = "npm run db:migrate"
+ ```
+
+ For independent commands, a named table runs them concurrently:
+ ```toml
+ [pre-start]
+ install = "npm install"
+ env = "cp .env.example .env"
+ ```
+
+4. **Preserve existing structure and comments**
+
+### Validation Before Adding Commands
+
+Before adding hooks, validate:
+
+```bash
+# Verify command exists
+which npm
+which cargo
+
+# For npm, verify script exists
+npm run lint --dry-run
+
+# For shell commands, check syntax
+bash -n -c "if [ true ]; then echo ok; fi"
+```
+
+**Dangerous patterns** — Warn users before creating hooks with:
+- Destructive commands: `rm -rf`, `DROP TABLE`
+- External dependencies: `curl http://...`
+- Privilege escalation: `sudo`
+
+## Permission Models
+
+### User Config: Conservative
+- **Never edit without consent** - Always show proposed change and wait for approval
+- **Never install tools** - Provide commands for users to run themselves
+- **Preserve structure** - Keep existing comments and organization
+- **Validate first** - Ensure TOML is valid before writing
+
+### Project Config: Proactive
+- **Create directly** - Changes are versioned, easily reversible
+- **Validate commands** - Check commands exist before adding
+- **Explain choices** - Add comments documenting why hooks exist
+- **Warn on danger** - Flag destructive operations before adding
+
+## Common Tasks Reference
+
+### User Config Tasks
+- Set up commit message generation → `reference/llm-commits.md`
+- Customize worktree paths → `reference/config.md#worktree-path-template`
+- Custom commit templates → `reference/llm-commits.md#prompt-templates`
+- Configure command defaults → `reference/config.md#command-config`
+- Set up personal hooks → `reference/config.md#hooks`
+
+### Project Config Tasks
+- Set up hooks for new project → `reference/hook.md`
+- Add hook to existing config → `reference/hook.md#hook-forms`
+- Use template variables → `reference/hook.md#template-variables`
+- Add dev server URL to list → `reference/config.md#dev-server-url`
+
+### Aliases & Multi-Worktree Tasks
+- Create a `wt` alias → `reference/extending.md#aliases`
+- Run a command in every worktree → `reference/step.md#wt-step-for-each`
+- Rebase every worktree (up-style) → `reference/extending.md#recipe-rebase-every-worktree-onto-its-upstream`
+- Defer a template variable to a nested `wt` command → `reference/extending.md#deferring-expansion-to-a-nested-wt-command`
+
+## Key Commands
+
+```bash
+# View all configuration
+wt config show
+
+# Create initial user config (LLM/commit setup: see reference/llm-commits.md)
+wt config create
+
+# Full config reference (subcommands, templates, env vars)
+wt config --help
+```
+
+## Loading Additional Documentation
+
+Load **reference files** for detailed configuration, hook specifications, and troubleshooting.
+
+Find specific sections with grep:
+```bash
+grep -A 20 "## Setup" reference/llm-commits.md
+grep -A 30 "### pre-start" reference/hook.md
+grep -A 20 "## Warning Messages" reference/shell-integration.md
+```
+
+## Hook Approvals in Non-Interactive Sessions
+
+Worktrunk never runs a project's hooks or aliases until the user has explicitly approved them. The commands in `.config/wt.toml` are arbitrary shell code shipped in a repository the user may have just cloned, so on first run Worktrunk shows each command and waits for the user to approve it — an untrusted `.config/wt.toml` cannot silently execute anything. Approvals are stored per-project in `~/.config/worktrunk/approvals.toml` and re-prompted whenever a command template changes, so a hook can't be swapped for a different command after it was approved.
+
+Agents running `wt merge`, `wt switch`, or other commands that trigger hooks will hit an error like:
+
+```
+▲ cargo-difftest needs approval to execute 1 command:
+○ post-merge install:
+ cargo install --path .
+✗ Cannot prompt for approval in non-interactive environment
+↳ To skip prompts in CI/CD, add --yes; to pre-approve commands, run wt config approvals add
+```
+
+The resolution is for the user to make the trust decision themselves:
+
+- **`wt config approvals add`** — interactive prompt where the user reviews each command before it is stored to `~/.config/worktrunk/approvals.toml`. Run once per project; the approval persists across invocations until the command template changes or the project moves. This is the path to recommend — the user reviews and consents to exactly the commands that will run.
+
+**When invoked as an agent, stop and escalate to the user.** Approving a project's hooks is a security decision about whether this repository should be trusted to run arbitrary commands on the user's machine — that decision belongs to the user, not the agent. Tell the user to run `wt config approvals add` and let them review the commands. Do not run `--yes` on the user's behalf: it skips the approval gate for that invocation, so reaching for it to unblock a command defeats the protection. `--yes` exists for CI/CD pipelines that already control their own hook contents; it is not a shortcut for an interactive agent to silence an approval prompt.
+
+## Advanced: Agent Handoffs
+
+When the user requests spawning a worktree with an agent in a background session ("spawn a worktree for...", "hand off to another agent"), use the appropriate pattern for their terminal multiplexer. Substitute `` with the CLI you are running as: `claude` for Claude Code, `'opencode run'` for OpenCode.
+
+**tmux** (check `$TMUX` env var):
+```bash
+tmux new-session -d -s "wt switch --create -x -- ''"
+```
+
+**Zellij** (check `$ZELLIJ` env var):
+```bash
+zellij run -- wt switch --create -x -- ''
+```
+
+**Requirements** (all must be true):
+- User explicitly requests spawning/handoff
+- User is in a supported multiplexer (tmux or Zellij)
+- The user's project instructions (`CLAUDE.md` or `AGENTS.md`) or an explicit prompt authorize this pattern
+
+**Do not use this pattern** for normal worktree operations.
+
+Example (tmux, Claude Code):
+```bash
+tmux new-session -d -s fix-auth-bug "wt switch --create fix-auth-bug -x claude -- \
+ 'The login session expires after 5 minutes. Find the session timeout config and extend it to 24 hours.'"
+```
+
+Example (Zellij, OpenCode):
+```bash
+zellij run -- wt switch --create fix-auth-bug -x 'opencode run' -- \
+ 'The login session expires after 5 minutes. Find the session timeout config and extend it to 24 hours.'
+```
+
+### Parallel sub-Agents (single Claude Code session)
+
+To spawn multiple sub-Agents that each work in their own worktree from one Claude Code session — no terminal multiplexer, no human in the other pane — pre-start each worktree from the parent and pass the path into the sub-Agent prompt:
+
+```bash
+wt switch --create --no-cd --no-hooks
+```
+
+Then call the `Agent` tool **without** `isolation: "worktree"`, naming the path in the prompt:
+
+```
+You are working in `/abs/path/to/worktrunk.` on branch ``.
+All edits must stay in that worktree.
+```
+
+`--no-cd` skips the shell-integration cd script the parent can't consume; `--no-hooks` is appropriate when each sub-Agent will run its own build/test step (e.g. `cargo run -- hook pre-merge --yes`) and you don't need post-start setup repeated per worktree.
+
+**Do not** use `Agent { isolation: "worktree" }` for this. Claude Code passes its internal agent ID as `name` to the `WorktreeCreate` hook, so `wt` creates the worktree as `worktrunk.agent-` on a throwaway branch. If the sub-Agent then creates a feature branch on top, you end up with non-canonical paths, orphan branches, and post-start hooks fired against the wrong branch. Pre-creating with `wt switch --create` keeps path, branch, and hook target aligned.
diff --git a/plugins/worktrunk/skills/worktrunk/reference/README.md b/plugins/worktrunk/skills/worktrunk/reference/README.md
new file mode 100644
index 0000000000..710ad98797
--- /dev/null
+++ b/plugins/worktrunk/skills/worktrunk/reference/README.md
@@ -0,0 +1,248 @@
+
+
+
Worktrunk
+
+
+[](https://worktrunk.dev)
+[](https://opensource.org/licenses/MIT)
+[](https://github.com/max-sixty/worktrunk/actions?query=branch%3Amain+workflow%3Aci)
+[](https://codecov.io/gh/max-sixty/worktrunk)
+[](https://github.com/max-sixty/worktrunk/stargazers)
+[](https://github.com/max-sixty/tend)
+
+> **July 2026**: Worktrunk was [released](https://x.com/max_sixty/status/2006077845391724739?s=20) at the start of the year, and has quickly become the most popular git worktree manager. It's built with love (there's no slop!). Please let me know any frictions at all; I'm intensely focused on continuing to make Worktrunk excellent, and the biggest help is folks posting problems they perceive.
+
+Worktrunk is a CLI for git worktree management, designed for running AI agents in parallel.
+
+Worktrunk's three core commands make worktrees as easy as branches. Plus, Worktrunk has a bunch of quality-of-life features to simplify working with many parallel changes, including hooks to automate local workflows.
+
+A quick demo:
+
+
+
+> ### 📚 Full documentation at [worktrunk.dev](https://worktrunk.dev) 📚
+
+
+
+## Context: git worktrees
+
+AI agents like Claude Code and Codex can handle longer tasks without
+supervision, such that it's possible to manage 5-10+ in parallel. Git's native
+worktree feature give each agent its own working directory, so they don't step
+on each other's changes.
+
+But the git worktree UX is clunky. Even a task as small as starting a new
+worktree requires typing the branch name three times: `git worktree add -b feat
+../repo.feat`, then `cd ../repo.feat`.
+
+## Worktrunk makes git worktrees as easy as branches
+
+Worktrees are addressed by branch name; paths are computed from a configurable template.
+
+> Start with the core commands
+
+**Core commands:**
+
+
+
+
+ | Task |
+ Worktrunk |
+ Plain git |
+
+
+
+
+ | Switch worktrees |
+ wt switch feat |
+ cd ../repo.feat |
+
+
+ | Create + start Claude |
+ wt switch -c -x claude feat |
+ git worktree add -b feat ../repo.feat && \
+cd ../repo.feat && \
+claude |
+
+
+ | Clean up |
+ wt remove |
+ cd ../repo && \
+git worktree remove ../repo.feat && \
+git branch -d feat |
+
+
+ | List with status |
+ wt list |
+ git worktree list (paths only) |
+
+
+
+
+> Expand into the more advanced commands as needed
+
+**Workflow automation:**
+
+- **[Hooks](https://worktrunk.dev/hook/)** — run commands on create, pre-merge, post-merge, etc
+- **[LLM commit messages](https://worktrunk.dev/llm-commits/)** — generate commit messages from diffs
+- **[Merge workflow](https://worktrunk.dev/merge/)** — squash, rebase, merge, clean up in one command
+- **[Interactive picker](https://worktrunk.dev/switch/#interactive-picker)** — browse worktrees with live diff and log previews
+- **[Copy build caches](https://worktrunk.dev/step/#wt-step-copy-ignored)** — skip cold starts by sharing `target/`, `node_modules/`, etc between worktrees
+- **[`wt list --full`](https://worktrunk.dev/list/#full-mode)** — [CI status](https://worktrunk.dev/list/#ci-status) and [AI-generated summaries](https://worktrunk.dev/list/#llm-summaries) per branch
+- **[PR checkout](https://worktrunk.dev/switch/#pull-requests-and-merge-requests)** — `wt switch pr:123` to jump straight to a PR's branch
+- **[Dev server per worktree](https://worktrunk.dev/tips-patterns/#dev-server-per-worktree)** — `hash_port` template filter gives each worktree a unique port
+- **[Aliases](https://worktrunk.dev/extending/#aliases) & [per-branch variables](https://worktrunk.dev/config/#wt-config-state-vars)** — custom `wt ` commands and branch-scoped state for hook templates
+- ...and **[lots more](#next-steps)**
+
+Multiple parallel agents, same simple commands:
+
+
+
+
+
+
+
+## Install
+
+**Homebrew (macOS & Linux):**
+
+```bash
+brew install worktrunk && wt config shell install
+```
+
+Shell integration allows commands to change directories.
+
+**Cargo:**
+
+```bash
+cargo install worktrunk && wt config shell install
+```
+
+
+Windows & other
+
+**Windows.** `wt` defaults to Windows Terminal's command, so Winget additionally installs Worktrunk as `git-wt` to avoid the conflict:
+
+```bash
+winget install max-sixty.worktrunk
+git-wt config shell install
+```
+
+Alternatively, disable Windows Terminal's alias (Settings → Apps → Advanced app settings → App execution aliases → "Terminal"/"Terminal Preview") to use `wt` directly.
+
+**Arch Linux:**
+
+```bash
+sudo pacman -S worktrunk && wt config shell install
+```
+
+**Conda / Pixi** (community-maintained [feedstock](https://github.com/conda-forge/worktrunk-feedstock)):
+
+```bash
+conda install -c conda-forge worktrunk && wt config shell install
+```
+
+Or with [Pixi](https://pixi.sh): `pixi global install worktrunk && wt config shell install`.
+
+
+
+## Quick start
+
+Create a worktree for a new feature:
+
+```console
+$ wt switch --create feature-auth
+✓ Created branch feature-auth from main and worktree @ ~/repo.feature-auth
+
+```
+
+This creates a new branch and worktree, then switches to it. Do your work, then check all worktrees with [`wt list`](https://worktrunk.dev/list/):
+
+```console
+$ wt list
+ Branch Status HEAD± main↕ main…± Remote⇅ Commit Age Message
+@ feature-auth + ↑ +27 -8 ↑1 +31 4bc72dc9 2h Add authenticat…
+^ main ^⇡ ⇡1 0e631add 1d Initial commit
+
+○ Showing 2 worktrees, 1 with changes, 1 ahead, 1 column hidden
+
+```
+
+The `@` marks the current worktree. `+` means staged changes, `↑1` means 1 commit ahead of main, `⇡` means unpushed commits.
+
+When done, either:
+
+**PR workflow** — commit, push, open a PR, merge via GitHub/GitLab, then clean up:
+
+```bash
+wt step commit # commit staged changes
+gh pr create # or glab mr create
+wt remove # after PR is merged
+```
+
+**Local merge** — squash, rebase onto main, fast-forward merge, clean up:
+
+```console
+$ wt merge main
+◎ Generating commit message and committing changes... (2 files, +53, no squashing needed)
+ Add authentication module
+✓ Committed changes @ a1b2c3d
+◎ Merging 1 commit to main @ a1b2c3d (no rebase needed)
+ * a1b2c3d Add authentication module
+ auth.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ lib.rs | 2 ++
+ 2 files changed, 53 insertions(+)
+✓ Merged to main (1 commit, 2 files, +53)
+◎ Removing feature-auth worktree & branch in background (same commit as main, _)
+○ Switched to worktree for main @ ~/repo
+
+```
+
+For parallel agents, create multiple worktrees and launch an agent in each:
+
+```bash
+wt switch -x claude -c feature-a -- 'Add user authentication'
+wt switch -x claude -c feature-b -- 'Fix the pagination bug'
+wt switch -x claude -c feature-c -- 'Write tests for the API'
+```
+
+The `-x` flag runs a command after switching; arguments after `--` are passed to it. Configure [post-start hooks](https://worktrunk.dev/hook/#hook-types) to automate setup (install deps, start dev servers).
+
+## Next steps
+
+- Learn the core commands: [`wt switch`](https://worktrunk.dev/switch/), [`wt list`](https://worktrunk.dev/list/), [`wt merge`](https://worktrunk.dev/merge/), [`wt remove`](https://worktrunk.dev/remove/)
+- Set up [hooks](https://worktrunk.dev/hook/) for automated setup
+- Explore [LLM commit messages](https://worktrunk.dev/llm-commits/), [interactive
+ picker](https://worktrunk.dev/switch/#interactive-picker), [Claude Code integration](https://worktrunk.dev/claude-code/), [CI
+ status & PR links](https://worktrunk.dev/list/#ci-status)
+- Browse [tips & patterns](https://worktrunk.dev/tips-patterns/) for recipes: aliases, dev servers, databases, agent handoffs, and more
+- [Extending Worktrunk](https://worktrunk.dev/extending/) — customize workflows with hooks & aliases
+- Run `wt --help` or `wt --help` for quick CLI reference
+
+## Further reading
+
+- [Claude Code: Best practices for agentic coding](https://www.anthropic.com/engineering/claude-code-best-practices) — Anthropic's official guide, including the worktree pattern
+- [Shipping faster with Claude Code and Git Worktrees](https://incident.io/blog/shipping-faster-with-claude-code-and-git-worktrees) — incident.io's workflow for parallel agents
+- [Git worktree pattern discussion](https://github.com/anthropics/claude-code/issues/1052) — Community discussion in the Claude Code repo
+- [@DevOpsToolbox's video on Worktrunk](https://youtu.be/WBQiqr6LevQ?t=345)
+- [git-worktree documentation](https://git-scm.com/docs/git-worktree) — Official git reference
+
+
+
+## Contributing
+
+- ⭐ Star the repo
+- Tell a friend about Worktrunk
+- [Open an issue](https://github.com/max-sixty/worktrunk/issues/new?title=&body=%23%23%20Description%0A%0A%3C!--%20Describe%20the%20bug%20or%20feature%20request%20--%3E%0A%0A%23%23%20Context%0A%0A%3C!--%20Any%20relevant%20context%3A%20your%20workflow%2C%20what%20you%20were%20trying%20to%20do%2C%20etc.%20--%3E) — feedback, feature requests, even a small friction or imperfect user message, or [a worktree pain not yet solved](https://github.com/max-sixty/worktrunk/issues/new?title=Worktree%20friction%3A%20&body=%23%23%20The%20friction%0A%0A%3C!--%20What%20worktree-related%20task%20is%20still%20painful%3F%20--%3E%0A%0A%23%23%20Current%20workaround%0A%0A%3C!--%20How%20do%20you%20handle%20this%20today%3F%20--%3E%0A%0A%23%23%20Ideal%20solution%0A%0A%3C!--%20What%20would%20make%20this%20easier%3F%20--%3E)
+- Share: [X](https://twitter.com/intent/tweet?text=Worktrunk%20%E2%80%94%20CLI%20for%20git%20worktree%20management&url=https%3A%2F%2Fworktrunk.dev) · [Reddit](https://www.reddit.com/submit?url=https%3A%2F%2Fworktrunk.dev&title=Worktrunk%20%E2%80%94%20CLI%20for%20git%20worktree%20management) · [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fworktrunk.dev)
+
+> ### 📚 Full documentation at [worktrunk.dev](https://worktrunk.dev) 📚
+
+### Star history
+
+
+
+
diff --git a/plugins/worktrunk/skills/worktrunk/reference/claude-code.md b/plugins/worktrunk/skills/worktrunk/reference/claude-code.md
new file mode 100644
index 0000000000..955ad72a9e
--- /dev/null
+++ b/plugins/worktrunk/skills/worktrunk/reference/claude-code.md
@@ -0,0 +1,125 @@
+# Agent Integration
+
+Worktrunk ships a plugin for each supported agent CLI. What a plugin provides depends on the hooks that CLI exposes:
+
+| Capability | Claude Code | Codex | OpenCode | Gemini CLI |
+|---|:-:|:-:|:-:|:-:|
+| Configuration skill | ✓ | ✓ | | ✓ |
+| Activity tracking (🤖/💬 in `wt list`) | ✓ | ✓ | ✓ | ✓ |
+| Worktree isolation | ✓ | | | |
+| `/wt-switch-create` command | ✓ | | | |
+
+The configuration skill is documentation the agent reads to help set up LLM commits, hooks, and troubleshooting. Activity tracking shows which worktrees have running sessions. Worktree isolation needs worktree-lifecycle hooks and `/wt-switch-create` needs session working-directory switching — both Claude Code-only, so Codex, OpenCode, and Gemini users invoke `wt switch --create` and `wt remove` directly. Codex tracks activity through its own turn-end (`Stop`) hook, but has no session-exit event, so a marker persists after a Codex session ends until the next session or a manual `wt config state marker clear`.
+
+## Installation
+
+### Claude Code
+
+```bash
+wt config plugins claude install
+```
+
+Manual equivalent:
+
+```bash
+claude plugin marketplace add max-sixty/worktrunk
+claude plugin install worktrunk@worktrunk
+```
+
+### Codex
+
+```bash
+wt config plugins codex install
+```
+
+This configures the Worktrunk marketplace in Codex. Then run `/plugins` in Codex and install Worktrunk from the marketplace. Manual equivalent:
+
+```bash
+codex plugin marketplace add max-sixty/worktrunk
+```
+
+To remove the marketplace entry, run `wt config plugins codex uninstall`. Already-installed plugins are left unchanged.
+
+### OpenCode
+
+```bash
+wt config plugins opencode install
+```
+
+This writes the activity-tracking plugin to OpenCode's global plugins directory, `~/.config/opencode/plugins/worktrunk.ts` (honoring `$OPENCODE_CONFIG_DIR` and `$XDG_CONFIG_HOME`). `wt config plugins opencode uninstall` removes it.
+
+### Gemini CLI
+
+```bash
+gemini extensions install https://github.com/max-sixty/worktrunk
+```
+
+Gemini loads the extension natively from the repository, so there is no `wt` wrapper. `gemini extensions uninstall worktrunk` removes it.
+
+## Configuration skill
+
+With the `/worktrunk` skill, the agent can help with:
+
+- Setting up LLM-generated commit messages
+- Adding project hooks (pre-start, pre-merge, pre-commit)
+- Configuring worktree path templates
+- Fixing shell integration issues
+
+Claude Code is designed to load the skill automatically when it detects worktrunk-related questions.
+
+## Activity tracking
+
+The Claude Code, Codex, OpenCode, and Gemini plugins track agent sessions with status markers in `wt list`:
+
+```bash
+$ wt list
+ Branch Status HEAD± main↕ main…± Remote⇅ Path Commit Age Message
+@ main ^⇡ ⇡1 . 33323bc1 1d Initial commit
++ feature-api ↑ 🤖 ↑1 +1 ../repo.feature-api 70343f03 1d Add REST API endpoints
++ review-ui ? ↑ 💬 ↑1 +1 ../repo.review-ui a585d6ed 1d Add dashboard component
++ wip-docs ? – ../repo.wip-docs 33323bc1 1d Initial commit
+
+○ Showing 4 worktrees, 2 with changes, 2 ahead
+```
+
+- 🤖 — agent is working
+- 💬 — agent is waiting or idle
+
+The Claude Code, OpenCode, and Gemini plugins clear the marker when a session ends. A stale marker can remain if the agent process is killed before its session-end hook runs. Codex exposes no session-exit event, so its marker always persists after a session ends. In every case, `wt config state marker clear` removes a marker manually.
+
+### Manual status markers
+
+Set status markers manually for any workflow:
+
+```bash
+$ wt config state marker set "🚧" # Current branch
+$ wt config state marker set "✅" --branch feature # Specific branch
+$ git config worktrunk.state.feature.marker '{"marker":"💬","set_at":0}' # Direct
+```
+
+## Worktree isolation (Claude Code only)
+
+Claude Code agents can run in isolated worktrees (`isolation: "worktree"`). By default, Claude Code creates these with `git worktree add`. The plugin's `WorktreeCreate` and `WorktreeRemove` hooks route this through `wt switch --create` and `wt remove` instead, so worktrees created by agents get worktrunk's naming conventions, hooks, and lifecycle management.
+
+## `/wt-switch-create` command (Claude Code only)
+
+`/wt-switch-create [] [] [-- ]` starts a task in a fresh worktree without leaving the session: it creates the worktree, switches into it, and runs the task (all arguments optional). The worktree persists like any other; merge or remove it with `wt merge` / `wt remove`.
+
+## Statusline (Claude Code only)
+
+`wt list statusline --format=claude-code` outputs a single-line status for the Claude Code statusline. When the CI status cache is stale, this fetches from the network — typically 1–2 seconds — making it suitable for async statuslines but too slow for synchronous shell prompts. If a faster version would be helpful, please [open an issue](https://github.com/max-sixty/worktrunk/issues).
+
+~/w/myproject.feature-auth !🤖 @+42 -8 ↑3 ⇡1 #3035 Opus 🌔 65% 1.4×(10am–3pm)
+
+When Claude Code provides context window usage via stdin JSON, a moon phase gauge appears (🌕→🌑 as context fills). A `×()` segment appears when Claude's 5-hour or weekly rate limit is on track to be hit before reset — `1.4×(10am–3pm)` reads as 1.4× the pace that would exactly fill that window. Its colour deepens with severity — dim, then dim-yellow, then yellow — as more of the window would be spent locked out at the cap, so a fast pace that would only tip over near the reset stays dim. Above 90% used it shows usage instead of pace — `93%(10am–3pm)` — near the cap, how much is left matters more than how fast it's going.
+
+Add to `~/.claude/settings.json`:
+
+```json
+{
+ "statusLine": {
+ "type": "command",
+ "command": "wt list statusline --format=claude-code"
+ }
+}
+```
diff --git a/plugins/worktrunk/skills/worktrunk/reference/code-signing.md b/plugins/worktrunk/skills/worktrunk/reference/code-signing.md
new file mode 100644
index 0000000000..b707521a09
--- /dev/null
+++ b/plugins/worktrunk/skills/worktrunk/reference/code-signing.md
@@ -0,0 +1,52 @@
+# Code Signing Policy
+
+This page is Worktrunk's **code signing policy**. It describes what gets signed, which certificate is used, how the signing pipeline works, and who authorizes each release. It exists both to document the process for users and to satisfy the transparency requirements of the [SignPath Foundation](https://signpath.org/) open-source code signing program.
+
+## Why signing matters
+
+Worktrunk's Windows binaries (`wt.exe` and `git-wt.exe`) are small, native executables. Microsoft Defender's machine-learning heuristics routinely flag unsigned native executables of this shape as generic threats (for example `Trojan:Win32/Wacatac.B!ml`) — a false positive driven by the *absence of a trusted signature*, not by anything in the code. A valid [Authenticode](https://learn.microsoft.com/en-us/windows-hardware/drivers/install/authenticode) signature gives Defender's cloud something signed to trust, which is what stops that class of false positive. Signing the Windows artifacts is the durable fix.
+
+The macOS and Linux release artifacts, the crates.io source distribution, and `cargo install` builds are unaffected by this policy — `cargo install` compiles locally from source and never downloads a pre-built artifact.
+
+## Signing certificate
+
+> Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/).
+
+The certificate's private key is generated and held on SignPath's Hardware Security Module (HSM); no maintainer ever possesses the private key. Signing happens only through SignPath's service, invoked from Worktrunk's release pipeline.
+
+## What is signed
+
+- `wt.exe` and `git-wt.exe` — the Windows binaries shipped in the `x86_64-pc-windows-msvc` release archive on each [GitHub Release](https://github.com/max-sixty/worktrunk/releases) and distributed via [winget](https://github.com/microsoft/winget-pkgs) (`winget install max-sixty.worktrunk`). `git-wt.exe` is the same program built as a git subcommand.
+
+Nothing else is signed under this policy. Signed artifacts contain only code built from this repository; any bundled third-party libraries are used unmodified.
+
+## Build and signing pipeline
+
+1. Every release is triggered by pushing a version tag to [`max-sixty/worktrunk`](https://github.com/max-sixty/worktrunk). No release is built from any other source.
+2. The [`release` workflow](https://github.com/max-sixty/worktrunk/blob/main/.github/workflows/release.yaml) builds the platform binaries with [cargo-dist](https://axodotdev.github.io/cargo-dist/) on GitHub-hosted runners, from the tagged commit only.
+3. The Windows binary is submitted to SignPath for signing via the [SignPath GitHub Action](https://github.com/SignPath/github-action-submit-signing-request). The signed binary is returned to the workflow and published in the release archive.
+4. The build is reproducible from public source: the workflow, the toolchain pin ([`rust-toolchain.toml`](https://github.com/max-sixty/worktrunk/blob/main/rust-toolchain.toml)), and the release configuration ([`dist-workspace.toml`](https://github.com/max-sixty/worktrunk/blob/main/dist-workspace.toml)) are all tracked in this repository.
+
+## Project roles
+
+Worktrunk follows SignPath's team model:
+
+| Role | Responsibility | Who |
+|------|----------------|-----|
+| **Author** | Trusted developer who commits and tags releases | [@max-sixty](https://github.com/max-sixty) |
+| **Reviewer** | Reviews contributions from outside the trusted set before they can be released | [@max-sixty](https://github.com/max-sixty) |
+| **Approver** | Authorizes each signing request for a release | [@max-sixty](https://github.com/max-sixty) |
+
+All maintainers with signing authority use multi-factor authentication on their GitHub and SignPath accounts.
+
+## Release approval
+
+Every signing request requires **manual approval** by an Approver before the certificate is applied — signing is never fully automated. A release is signed only after the Approver has confirmed the artifact was built from the tagged commit in this repository.
+
+## Privacy
+
+Worktrunk is a local-first command-line tool. It performs no telemetry, collects no analytics, and transmits no user data. Network access happens only when a command the user ran needs it (for example, fetching CI status for `wt list --full`); the [FAQ](https://worktrunk.dev/faq/) and inline documentation describe exactly when. The signed binaries add no data collection of any kind.
+
+## Reporting a problem
+
+If you believe a signed Worktrunk binary has been tampered with, or you receive an antivirus detection on an official release artifact, please [open an issue](https://github.com/max-sixty/worktrunk/issues). For a suspected Defender false positive, you can also report the file to Microsoft through the [Windows Defender submission portal](https://www.microsoft.com/en-us/wdsi/filesubmission) (as *"I believe this file is safe"*), which corrects the cloud definition for every user.
diff --git a/plugins/worktrunk/skills/worktrunk/reference/config.md b/plugins/worktrunk/skills/worktrunk/reference/config.md
new file mode 100644
index 0000000000..09048e09bb
--- /dev/null
+++ b/plugins/worktrunk/skills/worktrunk/reference/config.md
@@ -0,0 +1,1468 @@
+# wt config
+
+Manage user & project configs. Includes shell integration, hooks, and saved state.
+
+## Examples
+
+Install shell integration (required for directory switching):
+
+```bash
+$ wt config shell install
+```
+
+Create user config file with documented examples:
+
+```bash
+$ wt config create
+```
+
+Create project config file (`.config/wt.toml`) for hooks:
+
+```bash
+$ wt config create --project
+```
+
+Show current configuration and file locations:
+
+```bash
+$ wt config show
+```
+
+## Configuration files
+
+| File | Location | Contains | Committed & shared |
+|------|----------|----------|--------------------|
+| **User config** | `~/.config/worktrunk/config.toml` | Worktree path template, LLM commit configs, etc | ✗ |
+| **Project config** | `.config/wt.toml` | Project hooks, dev server URL | ✓ |
+
+Organizations can deploy a system-wide config file for shared defaults — run `wt config show` for the platform-specific location.
+
+**User config** — personal preferences:
+
+```toml
+# ~/.config/worktrunk/config.toml
+worktree-path = ".worktrees/{{ branch | sanitize }}"
+
+[commit.generation]
+command = "MAX_THINKING_TOKENS=0 claude -p --no-session-persistence --model=haiku --tools='' --safe-mode --setting-sources='user' --system-prompt=''"
+```
+
+**Project config** — shared team settings:
+
+```toml
+# .config/wt.toml
+[pre-start]
+deps = "npm ci"
+
+[pre-merge]
+test = "npm test"
+```
+
+
+# User Configuration
+
+Create with `wt config create`. Values shown are defaults unless noted otherwise.
+
+Location:
+
+- macOS/Linux: `~/.config/worktrunk/config.toml` (or `$XDG_CONFIG_HOME` if set)
+- Windows: `%APPDATA%\worktrunk\config.toml`
+
+## Worktree path template
+
+Controls where new worktrees are created.
+
+**Available template variables:**
+
+- `{{ repo_path }}` — absolute path to the repository root (e.g., `/Users/me/code/myproject`. Or for bare repos, the bare directory itself)
+- `{{ repo }}` — repository directory name (e.g., `myproject`)
+- `{{ owner }}` — primary remote owner path (may include subgroups like `group/subgroup`)
+- `{{ branch }}` — raw branch name (e.g., `feature/auth`)
+- `{{ branch | sanitize }}` — filesystem-safe: `/` and `\` become `-` (e.g., `feature-auth`)
+- `{{ branch | sanitize_db }}` — database-safe: lowercase, underscores, hash suffix (e.g., `feature_auth_x7k`)
+- `{{ branch | codename(2) }}` — deterministic friendly name from a ~1.26M-combo pool (e.g., `malleable-opah`)
+
+This is a smaller set than [the variables hooks and aliases get](https://worktrunk.dev/hook/#template-variables).
+
+**Examples** for repo at `~/code/myproject`, branch `feature/auth`:
+
+Default — sibling directory (`~/code/myproject.feature-auth`):
+
+```toml
+worktree-path = "{{ repo_path }}/../{{ repo }}.{{ branch | sanitize }}"
+```
+
+Inside the repository (`~/code/myproject/.worktrees/feature-auth`):
+
+```toml
+worktree-path = "{{ repo_path }}/.worktrees/{{ branch | sanitize }}"
+```
+
+Friendly branch-derived names (`~/code/myproject.malleable-opah`):
+
+```toml
+worktree-path = "{{ repo_path }}/../{{ repo }}.{{ branch | codename(2) }}"
+```
+
+Friendly names with branch identity in a parent directory (`~/code/worktrees/feature-auth/malleable-opah`):
+
+```toml
+worktree-path = "{{ repo_path }}/../worktrees/{{ branch | sanitize }}/{{ branch | codename(2) }}"
+```
+
+Centralized worktrees directory (`~/worktrees/myproject/feature-auth`):
+
+```toml
+worktree-path = "~/worktrees/{{ repo }}/{{ branch | sanitize }}"
+```
+
+By remote owner path (`~/development/max-sixty/myproject/feature/auth`):
+
+```toml
+worktree-path = "~/development/{{ owner }}/{{ repo }}/{{ branch }}"
+```
+
+Bare repository (`~/code/myproject/feature-auth`):
+
+```toml
+worktree-path = "{{ repo_path }}/../{{ branch | sanitize }}"
+```
+
+`~` expands to the home directory. Relative paths resolve from `repo_path`.
+
+## LLM commit messages
+
+Generate commit messages automatically during merge. Requires an external CLI tool.
+
+### Claude Code
+
+```toml
+[commit.generation]
+command = "MAX_THINKING_TOKENS=0 claude -p --no-session-persistence --model=haiku --tools='' --safe-mode --setting-sources='user' --system-prompt=''"
+```
+
+### Codex
+
+```toml
+[commit.generation]
+command = "codex exec -m gpt-5.6-luna -c model_reasoning_effort='low' -c system_prompt='' --sandbox=read-only --json - | jq -sr '[.[] | select(.item.type? == \"agent_message\")] | last.item.text'"
+```
+
+### OpenCode
+
+```toml
+[commit.generation]
+command = "opencode run -m anthropic/claude-haiku-4.5 --variant fast"
+```
+
+### llm
+
+```toml
+[commit.generation]
+command = "llm -m claude-haiku-4.5"
+```
+
+### aichat
+
+```toml
+[commit.generation]
+command = "aichat -m claude:claude-haiku-4.5"
+```
+
+See [LLM commits docs](https://worktrunk.dev/llm-commits/) for setup and [Custom prompt templates](#custom-prompt-templates) for template customization.
+
+## Command config
+
+### List
+
+Persistent flag values for `wt list`. Override on command line as needed.
+
+```toml
+[list]
+summary = false # Enable LLM branch summaries (requires [commit.generation])
+
+full = false # Show CI status and LLM summaries (--full)
+branches = false # Include branches without worktrees (--branches)
+remotes = false # Include remote-only branches (--remotes)
+
+json-schema = 2 # JSON output schema: 2 (envelope) or 1 (bare array, the current default); unset emits 1 with a warning
+
+columns = ["branch", "status", "ci", "path"] # Columns to show, in order — built-ins or custom headers (omit for the default set)
+
+task-timeout-ms = 0 # Kill individual git commands after N ms; 0 disables
+timeout-ms = 0 # Wall-clock budget for the entire collect phase; 0 disables
+```
+
+`columns` selects and orders the columns to render; omit it for the default set.
+It is meant to drive a per-invocation [alias](https://worktrunk.dev/extending/#aliases)
+(`wt --config-set 'list.columns=[…]' list`), giving a named view without
+disturbing the default `wt list`. A static setting works but pins one layout
+over a table that otherwise adapts to `--full` and terminal width.
+
+Valid built-in names:
+
+- `branch` — The branch name
+- `status` — Git status symbols, plus any user-defined status
+- `working-diff` — Uncommitted line changes against `HEAD` (header `HEAD±`)
+- `ahead-behind` — Commits ahead of and behind the default branch (header `main↕`)
+- `branch-diff` — Line changes against the default branch (header `main…±`)
+- `summary` — An LLM-generated summary of the branch
+- `upstream` — Commits ahead of and behind the upstream tracking branch (header `Remote⇅`)
+- `ci` — CI status of the head commit
+- `path` — The worktree's path
+- `url` — Dev-server URL from the `[list] url` template
+- `commit` — The head commit's short hash
+- `age` — Time since the last commit
+- `message` — The head commit's subject
+
+A selection mixes built-ins with [custom columns](#custom-columns), each named
+by its `[list.custom-columns]` header (`columns = ["branch", "Ticket", "ci"]`),
+and is exhaustive: only the listed columns render. Omit `columns` to keep the
+default set, where custom columns append automatically. A built-in name wins a
+header collision; the gutter type indicator always shows.
+
+Listing a column forces it on, space permitting: `ci` shows without `--full`,
+since `--full` only bundles columns into the default table rather than gating a
+named one. A column whose data source is missing still stays hidden — `summary`
+needs an LLM command (`[commit.generation]`), `url` needs a `[list] url`
+template — since listing can't supply the data.
+
+The selection drives the table and the `wt switch` picker. `wt list --format
+json` always emits every field, but a listed gated column (`ci`, `summary`)
+still forces its data collection on, so the JSON carries the same data the
+table shows.
+
+#### Custom columns [experimental]
+
+Custom columns add per-branch context to the `wt list` table. Each
+`[list.custom-columns]` entry is a column: the key is the header, the template
+renders each row's cell.
+
+```toml
+[list.custom-columns.Ticket]
+template = "{{ vars.ticket }}" # Required; the result is the cell text
+width = 20 # Optional max display width (default: 40)
+priority = 9 # Optional drop order when the terminal narrows;
+ # lower = kept longer (default: 9, the URL band)
+```
+
+Templates may reference `{{ branch }}`, `{{ worktree_path }}`,
+`{{ worktree_name }}` (empty for branch-only rows), and two per-branch
+namespaces:
+
+- `{{ vars.* }}` — values stored with
+ [`wt config state vars set`](https://worktrunk.dev/config/#wt-config-state-vars).
+- `{{ git.branch.* }}` — the branch's own git config under `branch..*`,
+ read straight from `git config` (e.g. `{{ git.branch.jira }}` for a key you
+ set yourself, or the git-native `description`). Git lowercases config variable
+ names, so `branch..nvciShelf` reads as `{{ git.branch.nvcishelf }}`.
+
+All standard filters work (`sanitize`, `hash_port`, `codename`, …). A row
+where the template renders empty (e.g. a branch without the key) shows an
+empty cell; a column that is empty for every row is dropped from the table.
+`wt list --format json` includes the rendered values under `columns`.
+
+A `Jira` column reading a key kept in git config, and a `Summary` column
+showing just the first line of the git-native branch description:
+
+```toml
+[list.custom-columns.Jira]
+template = "{{ git.branch.jira }}"
+
+[list.custom-columns.Summary]
+template = "{{ git.branch.description | lines | first }}"
+```
+
+### Commit
+
+Shared by `wt step commit`, `wt step squash`, and `wt merge`.
+
+```toml
+[commit]
+stage = "all" # What to stage before commit: "all", "tracked", or "none"
+```
+
+### Merge
+
+Most flags are on by default. Set to false to change default behavior.
+
+```toml
+[merge]
+squash = true # Squash commits into one (--no-squash to preserve history)
+commit = true # Commit uncommitted changes first (--no-commit to skip)
+rebase = true # Rebase onto target before merge (--no-rebase to skip)
+remove = true # Remove worktree after merge (--no-remove to keep)
+verify = true # Run project hooks (--no-hooks to skip)
+ff = true # Fast-forward merge (--no-ff to create a merge commit instead)
+```
+
+### Remove
+
+Persistent flag values for `wt remove`. Override on command line as needed.
+
+```toml
+[remove]
+delete-branch = true # Delete branch after removal (--no-delete-branch to keep)
+```
+
+### Switch
+
+```toml
+[switch]
+cd = true # Change directory after switching (--no-cd to skip)
+
+[switch.picker]
+pager = "delta --paging=never" # Example: override git's core.pager for diff preview
+```
+
+### Step
+
+```toml
+[step.copy-ignored]
+exclude = [] # Additional excludes (e.g., [".cache/", ".turbo/"])
+```
+
+Built-in excludes always apply: VCS metadata directories (`.bzr/`, `.hg/`, `.jj/`, `.pijul/`, `.sl/`, `.svn/`) and tool-state directories (`.conductor/`, `.entire/`, `.worktrees/`). User config and project config exclusions are combined.
+
+### Aliases
+
+Command templates that run as `wt `. See the [Extending Worktrunk guide](https://worktrunk.dev/extending/#aliases) for usage and flags.
+
+```toml
+[aliases]
+greet = "echo Hello from {{ branch }}"
+url = "echo http://localhost:{{ branch | hash_port }}"
+```
+
+Aliases defined here apply to all projects. For project-specific aliases, use the [project config](https://worktrunk.dev/config/#project-configuration) `[aliases]` section instead.
+
+### User project-specific settings
+
+User config can include a `[projects]` table for project-specific settings — worktree layout, setting overrides, anything else — separate from the [project config](https://worktrunk.dev/config/#project-configuration) shared with teammates.
+
+Entries are keyed by project identifier — `//` derived from the primary remote URL (no `.git` suffix), or the canonical repo path when there is no remote. Run `wt config show` inside the repo to see the identifier for the current project; it appears in the `PROJECT CONFIG` section as `Identifier: …`.
+
+Scalar values (like `worktree-path`) replace the global value; everything else (hooks, aliases, etc.) appends, global first.
+
+```toml
+[projects."github.com/user/repo"]
+worktree-path = ".worktrees/{{ branch | sanitize }}"
+list.full = true
+merge.squash = false
+remove.delete-branch = false
+pre-start.env = "cp .env.example .env"
+step.copy-ignored.exclude = [".repo-local-cache/"]
+aliases.deploy = "make deploy BRANCH={{ branch }}"
+```
+
+Hooks support all three [hook forms](https://worktrunk.dev/hook/#hook-forms). A table runs multiple commands concurrently; an array-of-tables pipeline runs steps in sequence. The dotted-key examples below are equivalent to the table forms — TOML treats `projects."github.com/user/repo".post-start.server = "..."` and a `[projects."github.com/user/repo".post-start]` table the same way:
+
+```toml
+# Single command
+[projects."github.com/user/repo"]
+post-start = "mise trust"
+
+# Multiple commands, running concurrently
+[projects."github.com/user/repo".post-start]
+mise = "mise trust"
+server = "npm run dev"
+
+# Pipeline: steps run in sequence
+[[projects."github.com/user/repo".post-start]]
+install = "npm ci"
+
+[[projects."github.com/user/repo".post-start]]
+build = "npm run build"
+server = "npm run dev"
+```
+
+### Custom prompt templates
+
+Templates use [minijinja](https://docs.rs/minijinja/) syntax.
+
+#### Commit template
+
+Available variables:
+
+- `{{ git_diff }}`, `{{ git_diff_stat }}` — diff content
+- `{{ branch }}`, `{{ repo }}` — context
+- `{{ recent_commits }}` — recent commit messages
+- `{{ user_guidance }}`, `{{ project_guidance }}` — rendered append fragments (see [Appending to the prompt](https://worktrunk.dev/config/#appending-to-the-prompt))
+
+Default template:
+
+
+```toml
+[commit.generation]
+template = """
+Write a commit message for the staged changes below.
+
+
+- Subject line under 50 chars
+- For material changes, add a blank line then a body paragraph explaining the change
+- Output only the commit message, no quotes or code blocks
+
+
+
+{% if user_guidance %}
+
+{{ user_guidance }}
+
+{% endif %}{% if project_guidance %}
+
+{{ project_guidance }}
+
+{% endif %}
+
+{{ git_diff_stat }}
+
+
+
+{{ git_diff }}
+
+
+
+Branch: {{ branch }}
+{% if recent_commits %}
+{% for commit in recent_commits %}- {{ commit }}
+{% endfor %}{% endif %}
+
+
+"""
+```
+
+
+#### Squash template
+
+Available variables (in addition to commit template variables):
+
+- `{{ commit_details }}` — list of commits being squashed; each renders as its subject and exposes `.subject` / `.body`
+- `{{ target_branch }}` — merge target branch
+
+Default template:
+
+
+```toml
+[commit.generation]
+squash-template = """
+Write a commit message for the combined effect of these commits.
+
+
+- Subject line under 50 chars
+- For material changes, add a blank line then a body paragraph explaining the change
+- Output only the commit message, no quotes or code blocks
+
+
+
+{% if user_guidance %}
+
+{{ user_guidance }}
+
+{% endif %}{% if project_guidance %}
+
+{{ project_guidance }}
+
+{% endif %}
+
+{% for detail in commit_details %}- {{ detail.subject }}
+{% endfor %}
+
+
+{{ git_diff_stat }}
+
+
+
+{{ git_diff }}
+
+
+"""
+```
+
+
+#### Appending to the prompt [experimental]
+
+`template-append` adds to the prompt instead of replacing it. The value is rendered as its own minijinja template (same variables) and injected into the default templates' `{{ user_guidance }}` slot — a `` block right after `