Files
max-sixty__worktrunk/dev/config.example.toml
Maximilian Roos 89326f14af Publish shell integration, add a footer, dedupe global options (#4000)
Guides and site presentation: one home per topic, a shell-integration
page, a footer, and three rendering fixes.

**Global options were emitted once per command reference**

clap repeats the same ~20-line `Global Options:` block in every
reference it renders, so a page assembled from subdocs stacked 11 copies
on `/config/` and 13 on `/step/`. That padded the pages and gave site
search that many near-identical hits — "squash" returned both
`#command-reference` and `#command-reference-2`. `take_global_options`
cuts each reference at the heading as it is built, keeping only the
first; one `kept` flag threads through the subdoc expansion and the page
streams out rather than accumulating. Terminal `--help` renders through
clap directly and is unchanged.

The config page also carried colliding anchors — two "Hooks" (`#hooks`,
`#hooks-1`), two "Aliases", and seven "Examples" (`#examples` …
`#examples-6`) — now qualified at their source in `src/cli/config.rs`:
User/Project hooks, User/Project aliases, and
Approval/Alias/State/Cache/Log/Variable examples.

`/step/` still has its own set (eight "Examples", two "Options", two
"Arguments", plus "Staging" and "Dry run" pairs). Qualifying those moves
existing `/step/#examples-N` anchors, so it wants a pass of its own with
the inbound links audited; the deduplication above already removes 13
Global Options blocks from that page.

**Shell integration has a page**

Shell-integration debugging was skill-only: five named warning messages,
a PowerShell checklist, and the wrapper mechanism, with no site page —
while the FAQ's answer to "`wt switch` didn't cd" was to install the
Claude Code plugin. It is now `/shell-integration/`, offered first, with
the plugin as the second route. The `llms.txt` listing serves every page
as `/<slug>.md` from a hand-created symlink, so a new page was a 404 the
listing still advertised; the symlink is added and the sync now fails
when a listed page has none.

**Presentation**

- A site footer carries the version (read from `Cargo.toml` at build
time), releases, changelog and license. No page named any of them, and
`/code-signing/` was reachable only from inside a collapsed block on the
homepage. Starlight's `Footer` is wrapped rather than replaced.
- `wt list --full` renders 1157px inside an 800px content column, so 40%
of it sat behind a horizontal scrollbar with the pane beside the column
empty. A terminal frame now takes the whole pane where there is slack,
measured with a query container rather than recomputed from Starlight's
layout formula.
- The `wt-command-reference` frames offered a copy button for 3,877
characters of generated help text; they now expose no copy control. A
console block listing several commands is as often a menu of
alternatives as a recipe, and nothing in the markup tells them apart, so
every command line in such a block carries its own copy control
alongside the block's.
- The four command demos and the two hand-written figures get captions;
the 2.33 MB homepage GIF below the fold loads lazily.

**Sidebar order is pinned**

`site-navigation.mjs` told readers a
`test_sidebar_matches_frontmatter_order` would fail when the authored
sidebar and the pages' `sidebar.order` disagreed. No such test existed,
and the disagreement it describes is exactly what the survey found:
`remove` listed before `merge`, Agent integration ahead of
lower-numbered pages. The test is written, so the sidebar and the
`llms.txt` ordering derived from the frontmatter can't drift apart
again.

<details>
<summary>Guide corrections</summary>

- Tips & patterns was 26 flat H2 recipes in no order, all 26 in the
sidebar. They group under five H2s — setup and layout, aliases and
hooks, per-worktree services, working with agents, status/commits/logs —
with each recipe demoted to H3. Anchors are level-independent, so
existing `/tips-patterns/#…` fragments still resolve.
- `-x 'opencode run'` has been broken since 0.75.0 made `-x` a literal
program: it is `-x opencode -- run '<task>'`.
- The branch-summary preview moved from tab 5 to 6 when the unified-diff
tab landed; the recipe names the `summary` tab instead of a number.
- The Caddy recipe claimed `feature-auth` hashes to port 16460 — that is
`fix-auth`'s port. It is 18283.
- `_` in `wt list` is same-commit *and clean*; the
same-commit-with-changes glyph is `–`, which is not safe to delete.
- `wt step prune` removes branches with no worktree too, and the min-age
guard ages a worktree by its creation time and a bare branch by its
oldest reflog entry.
- `wt step eval -v` prints fifteen variables; the example showed two
under a lead calling them "the available template variables".
- A filter applied to `{{ vars.<key> }}` acts on the placeholder the
preview substitutes, so `{{ vars.port | default('8080') }}` previews as
`{{ vars.port }}`, filter gone.
- The `.git/wt/cache/` table was missing `picker-preview`, and `wt
config state clear` prompts unless `--yes`.
- `skills/worktrunk/reference/README.md` was a symlink to the repo
README that `SKILL.md` never referenced, and the plugin mirror
dereferenced it into a 262-line copy carrying the star-history token,
share links, and a logo path resolving nowhere. Nothing generated it, so
deleting the symlink is the whole fix.
- One home per topic: agent handoffs stay in tips-patterns, activity
markers in `claude-code.md`, alias-template deferral in `extending.md`,
and the `codename` filter's two `worktree-path` recipes give way to the
config page that owns path templates. The FAQ's "Running tests" and "How
can I contribute?" duplicated the README's Contributing block down to
the share URLs.
- The FAQ linked `/worktrunk/#install`, the `noindex` compatibility
route; the plugin hook shim's Windows Terminal hint pointed there too.
Both use `/#install`, where the new sidebar Install entry goes.
- Example names settle on `myproject` / `feature-auth`; "sibling to main
repo" becomes "sibling to the main worktree", and `wt remove`'s "target
worktree" becomes "the worktree being removed" per the project's own
terminology rule.

</details>

UX survey items: `#36`, `#37`, `#38`, `#39`, `#45`, `#47`, `#48`, `#49`,
`#50`, `#51`, `#52`, `#61`, `#94`, `#95`, `#96`, `#97`, `#99`, `#100`.

Reviewable files: the hand-written pages under `docs/src/content/docs/`
(notably the new `shell-integration.md`, `tips-patterns.md`, `faq.md`),
`docs/src/components/Footer.astro`,
`docs/src/plugins/worktrunk-terminal.mjs`,
`docs/src/site-navigation.mjs`, `docs/tests/*.mjs`, `src/help.rs`,
`plugins/worktrunk/hooks/wt.sh`. Generated mirrors and snapshots are
regenerated.

> _This was written by Claude Code on behalf of max-sixty_

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01XAUYWFN9d9oh6jyoQiouHb

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 12:44:47 -07:00

417 lines
18 KiB
TOML
Generated

# # 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`)
# - `{{ remote_repo }}` — repository name in the primary remote URL, without `.git` (e.g., `myproject`); differs from `{{ repo }}`, the directory on disk, when a clone was renamed
# - `{{ 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`):
#
# worktree-path = "{{ repo_path }}/../{{ repo }}.{{ branch | sanitize }}"
#
# Inside the repository (`~/code/myproject/.worktrees/feature-auth`):
#
# worktree-path = "{{ repo_path }}/.worktrees/{{ branch | sanitize }}"
#
# Friendly branch-derived names (`~/code/myproject.malleable-opah`):
#
# worktree-path = "{{ repo_path }}/../{{ repo }}.{{ branch | codename(2) }}"
#
# Friendly names with branch identity in a parent directory (`~/code/worktrees/feature-auth/malleable-opah`):
#
# worktree-path = "{{ repo_path }}/../worktrees/{{ branch | sanitize }}/{{ branch | codename(2) }}"
#
# Centralized worktrees directory (`~/worktrees/myproject/feature-auth`):
#
# worktree-path = "~/worktrees/{{ repo }}/{{ branch | sanitize }}"
#
# By remote owner path (`~/development/max-sixty/myproject/feature/auth`):
#
# worktree-path = "~/development/{{ owner }}/{{ repo }}/{{ branch }}"
#
# Bare repository cloned to `~/code/myproject/.git` (`~/code/myproject/feature-auth`):
#
# 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
#
# [commit.generation]
# command = "MAX_THINKING_TOKENS=0 claude -p --no-session-persistence --model=haiku --tools='' --safe-mode --setting-sources='user' --system-prompt=''"
#
# ### Codex
#
# [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
#
# [commit.generation]
# command = "opencode run -m anthropic/claude-haiku-4.5 --variant fast"
#
# ### llm
#
# [commit.generation]
# command = "llm -m claude-haiku-4.5"
#
# ### aichat
#
# [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.
#
# [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, default) or 1 (bare array)
#
# columns = ["branch", "status", "ci", "path"] # Columns to show, in order — built-ins or custom headers (omit for the default set)
#
# timeout-ms = 0 # Wall-clock budget for the entire collect phase; 0 disables
#
# `columns` selects and orders the columns the `wt list` table and the `wt switch`
# picker render; `--format json` ignores it and always emits every field. 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`, including untracked files (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.
#
# #### 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.
#
# [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.<name>.*`,
# 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.<name>.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
# `items[].display.columns` in schema 2, or under `columns` in schema 1.
#
# 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:
#
# [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`.
#
# [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.
#
# [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.
#
# [remove]
# delete-branch = true # Delete branch after removal (--no-delete-branch to keep)
#
# ### Switch
#
# [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
#
# [step.copy-ignored]
# exclude = [] # Additional excludes (e.g., [".cache/", ".turbo/"])
#
# Built-in excludes (VCS metadata and tool-state directories) always apply; the `wt step copy-ignored` docs (https://worktrunk.dev/step/#wt-step-copy-ignored) list them. User config and project config exclusions are combined.
#
# ### User aliases
#
# Command templates that run as `wt <name>`. See the Extending Worktrunk guide (https://worktrunk.dev/extending/#aliases) for usage and flags.
#
# [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-aliases) `[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 — `<host>/<owner>/<repo>` 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. See how the layers rank (https://worktrunk.dev/config/#precedence).
#
# [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 }}"
#
# #### Matching several repositories with one entry
#
# A key containing `*` matches any run of characters, `/` included, so one entry covers a whole host or namespace — including nested groups. `*` is the only wildcard; every other character, `.` among them, is literal.
#
# # Every repository on a self-hosted forge whose hostname carries no brand
# [projects."git.company.example/*"]
# forge.platform = "gitlab"
#
# # Everything under one namespace shares a layout
# [projects."git.company.example/platform/*"]
# worktree-path = ".worktrees/{{ branch | sanitize }}"
#
# Every matching entry applies, least- to most-specific, following the rule above: a more specific entry — `git.company.example/platform/*` over `git.company.example/*` — wins where both set the same setting, while hooks and aliases from every matching entry all run, least-specific first. A literal key is the most specific of all; specificity is the count of non-`*` characters in the key. End a host-wide key with `/*` — a bare `git.company.example*` also covers hosts whose names merely start with that string.
#
# `approved-commands` matches the same way, so a pattern entry approves its commands for every repository it covers. Only a key written by hand is ever a pattern: `wt config approvals add` and the interactive prompt record under the exact identifier, and `wt config approvals clear` removes only that exact entry, leaving a pattern other repositories share intact.
#
# #### Forge platform and hostname
#
# `forge` names the forge for the matched repositories — the user-level counterpart of the project config's forge platform (https://worktrunk.dev/config/#forge-platform) block, for a self-hosted host whose name carries no `github`, `gitlab`, or `gitea` for detection to read.
#
# [projects."git.company.example/*"]
# forge.platform = "gitlab" # or "github", "gitea" (experimental), "azure-devops" (experimental)
# forge.hostname = "api.git.company.example" # API host, when the remote's own host isn't it
#
# Both fields describe the host rather than the repository, which is why a pattern keyed to a hostname suits them, and why an SSH alias resolved through `~/.ssh/config` — where the name in the remote URL is local to one machine — belongs here rather than in a repository's committed config. A repository's own `[forge]` block still wins over any entry here, field by field: a repository that sets only `platform` still takes a matching entry's `hostname`.
#
# 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:
#
# # 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:
#
# <!-- DEFAULT_TEMPLATE_START -->
# [commit.generation]
# template = """
# <task>Write a commit message for the staged changes below.</task>
#
# <format>
# - 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
# </format>
#
# <style>
# - Imperative mood: "Add feature" not "Added feature"
# - Match recent commit style (conventional commits if used)
# - Describe the change, not the intent or benefit
# </style>
# {% if user_guidance %}
# <user-guidance>
# {{ user_guidance }}
# </user-guidance>
# {% endif %}{% if project_guidance %}
# <project-guidance>
# {{ project_guidance }}
# </project-guidance>
# {% endif %}
# <diffstat>
# {{ git_diff_stat }}
# </diffstat>
#
# <diff>
# {{ git_diff }}
# </diff>
#
# <context>
# Branch: {{ branch }}
# {% if recent_commits %}<recent_commits>
# {% for commit in recent_commits %}- {{ commit }}
# {% endfor %}</recent_commits>{% endif %}
# </context>
#
# """
# <!-- DEFAULT_TEMPLATE_END -->
#
# #### 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:
#
# <!-- DEFAULT_SQUASH_TEMPLATE_START -->
# [commit.generation]
# squash-template = """
# <task>Write a commit message for the combined effect of these commits.</task>
#
# <format>
# - 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
# </format>
#
# <style>
# - Imperative mood: "Add feature" not "Added feature"
# - Match the style of commits being squashed (conventional commits if used)
# - Describe the change, not the intent or benefit
# </style>
# {% if user_guidance %}
# <user-guidance>
# {{ user_guidance }}
# </user-guidance>
# {% endif %}{% if project_guidance %}
# <project-guidance>
# {{ project_guidance }}
# </project-guidance>
# {% endif %}
# <commits branch="{{ branch }}" target="{{ target_branch }}">
# {% for detail in commit_details %}- {{ detail.subject }}
# {% endfor %}</commits>
#
# <diffstat>
# {{ git_diff_stat }}
# </diffstat>
#
# <diff>
# {{ git_diff }}
# </diff>
#
# """
# <!-- DEFAULT_SQUASH_TEMPLATE_END -->
#
# #### Appending to the prompt
#
# `template-append` adds personal conventions to the commit and squash prompts without restating the whole template:
#
# [commit.generation]
# template-append = """
# - Explain the rationale in the body, not just the change
# """
#
# How the fragment renders, and the project-config counterpart: the LLM commits guide (https://worktrunk.dev/llm-commits/#appending-to-the-prompt).
#
# ## User hooks
#
# See `wt hook` (https://worktrunk.dev/hook/) for hook types, execution order, template variables, and examples. User hooks apply to all projects; project hooks (https://worktrunk.dev/config/#project-hooks) apply only to that repository.