Requested in [review feedback on
#3770](https://github.com/max-sixty/worktrunk/pull/3770#discussion_r3740664250):
> this is way too verbose; create another PR to add guidance to have the
share of the docs proportional to the share of the feature.
>
> in this case it's a tiny share of the feature and probably have zero
docs; it's implicit
The existing content principles in `docs/CLAUDE.md` govern what a piece
of help text says and where it starts, but nothing says how *much* a
behavior earns. That gap is what produced the paragraph the review was
reading: a narrow refusal in `wt remove` got a full paragraph on the
command's help page, ahead of behavior every user of the command meets.
New principle 5 makes the sizing rule explicit, including the zero case
— an edge case whose own error message states it at the moment it
matters is left implicit.
The paragraph that prompted this is already gone from #3770 (30ba949).
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
This reduces duplicated and false-confidence integration coverage while
preserving the suite's semantic and user-facing contracts.
## What changed
- Replaces three overlapping list-layout suites with two representative
CLI integrations, leaving exhaustive geometry at the direct layout
layer.
- Groups Git error render variants into labeled family snapshots and
removes command-by-shell wrapper cross-products while retaining
shell-specific conformance and regression cases.
- Updates test-authoring guidance around boundary choice, minimal
contrasts, and PTY use, and runs local and CI coverage through Nextest
isolation.
## Results
- Test catalog: 4,642 to 4,562
- Snapshots: 1,193 to 1,131
- Warm all-feature runtime: 84.70s to 78.17-80.35s
- Full coverage: 97.32% of lines
## Testing
- `cargo run -- hook pre-merge --yes`
- `cargo llvm-cov nextest --features shell-integration-tests
--summary-only`
> _This was written by Claude Code on behalf of max_.
Follow-up from #3568, which named these two as the one plain oversight
in its audit of `after_long_help` bodies that never reach a docs page.
`wt step rebase` and `wt step push` were the only two of twelve step
operations with no `<!-- subdoc: -->` marker, so their help was
terminal-only, and the only two bullets in `## Operations` left
unlinked. Both markers are added between `squash` and `diff`, per the
ordering invariant in `src/cli/step.rs`.
Both openers restated their `about` line, which reads as immediate
self-repetition on the web where `combine_command_docs()` concatenates
the two. Both bodies are rewritten.
## What reviewing the text against the code turned up
The old rebase body said "Conflicts abort immediately; use `git rebase
--abort` to recover", which is self-contradictory and wrong. Nothing
aborts: the worktree is left mid-rebase with git's conflict markers, and
git's output names `--continue`, `--skip`, and `--abort`. `wt merge`'s
pipeline step 3 carried the same sentence.
Four review passes then falsified nine more claims, every one reproduced
against a scratch repo before editing:
- **`--no-ff` runs no `git push` at all.** It builds the merge with
`commit-tree` + `update-ref` and syncs the worktree with `read-tree`; a
`-vv` trace shows zero `git push` invocations. The opener attributed the
whole command to `git push` with `--no-ff` as an example four lines
below. Its worktree sync is also best-effort, so the ref can move while
the worktree stays behind.
- **The Outcomes table was falsified by the upstream-swap topology.**
With local `main` diverged and behind `origin/main`, `wt step rebase
main` prints `Already up to date with origin/main` — `main` is not an
ancestor of the branch, and the result names a ref never typed.
- **"Nothing here reaches a remote" was false on a fresh clone.** With
no target argument and no cached `worktrunk.default-branch`,
`default_branch()` falls through to `git ls-remote`. The claim is now
"no commits leave the repository", which holds unconditionally.
- **The generated Arguments table contradicted the prose.** `[TARGET]
Target branch` sat two paragraphs below "the target is any commit-ish";
a blind reader nearly concluded rebase needs a branch name.
- **The table implied mutually exclusive rows.** `is_rebased_onto` is
checked first, and a branch at the target's tip satisfies both of the
first two.
- Plus: an orphan branch matched no row; "git's own output names the
ways out" is false under `advice.mergeConflict false`; "refused rather
than forced" raised the force question without closing it.
Adjacent, in files this change already touched: `wt merge`'s step 3
contradicted the new table and step 5 restated `wt step push`'s rule
with no link, so half the pipeline deferred and half repeated. Step 2
promised a backup ref unconditionally, but `create_safety_backup` runs
only when working-tree changes were staged — a clean-tree squash
rewrites commits and writes no `refs/wt-backup/` ref. That is a
data-safety claim, so it is corrected rather than left.
`docs/CLAUDE.md`'s subdoc "Use cases" cited `wt config create`, which
has no marker and no section.
## The code changes
**An annotated-tag target was never peeled.** Adding a tag example to
the rebase help is what made it worth running, and it did not work.
`is_rebased_onto` compared `git merge-base`, which peels an annotated
tag to its commit, against a bare `rev-parse`, which returns the tag
object's SHA. Those never match, so an annotated-tag target always
looked like it needed a rebase:
```
annotated v1.2.0 → "outcome": "rebased" (HEAD unchanged)
lightweight v1.2.0-lw → "outcome": "up_to_date" (same graph, same commit)
```
Fixed at the root by peeling with `^{commit}`, rather than documenting
the quirk. The test pairs the two tag types on one commit so the control
is a single factor away; it fails without the fix.
The reviewer caught that this test had gone missing, and it was right
about the consequence — without it, removing the `^{commit}` peel passes
CI. The cause was a merge resolution on this branch: `checkout --theirs`
on `tests/integration_tests/merge.rs` took main's whole file, dropping
`test_step_rebase_annotated_tag_is_peeled` along with the duplicate
squash test it was meant to drop. Restored, and re-verified in both
directions.
**`wt step push` ran out of a half-finished operation.** Mid-rebase, the
detached HEAD is a linear extension of the target, so the fast-forward
check passed and `wt step push main` printed `✓ Pushed to main (1
commit)` — moving the target branch onto a half-replayed history while
the worktree kept its conflict markers and the rebase stayed open. It
now runs the `ensure_no_operation_in_progress` gate `wt step rebase` and
`wt merge` have used since 84cbb0489.
#3579 landed while this was open and closed the same class for the
staging commands, with a better predicate for them: `git add -A`
collapses an unmerged path's stages, so the index is what knows, and an
index read also catches a conflicted `git stash pop` that writes no
state file. `wt step push` was scoped out of that, correctly — it stages
nothing, so an index read cannot speak for it. Its hazard is HEAD
itself, which is what the operation gate answers. The merge takes main's
`squash.rs` and its `test_step_squash_refuses_mid_merge` wholesale and
drops this branch's version of both; the gate's docstring now hands the
unresolved-conflict question to `WorkingTree::ensure_no_unmerged_paths`
instead of claiming it, and the rebase help enumerates all four gated
commands.
**A target worktree registered after its directory is gone got two
different answers.** The fast-forward died inside receive-pack — `fatal:
exec 'update-index': cd to '…' failed`, `! [remote rejected] HEAD ->
main (Up-to-date check failed)` — with the ref untouched, while
`--no-ff` moved the ref with plumbing of its own and skipped the sync,
so it succeeded over the same broken registration. Both refuse now with
the branch named and `git worktree prune` as the remedy, which is what
retires the two `.exists()` checks that papered over the state
downstream. `--no-ff` succeeding here is the one behavior this takes
away; a stale registration is worth one error rather than half a
success.
## Found and not fixed here
**Ignored files in a target worktree are silently overwritten, and `git
worktree lock` does not stop it.** `git status --porcelain` omits
ignored files, so the overlap check cannot see them and the stash does
not take them. Reproduced against the FAQ's own example
(`docs/content/faq.md:180` recommends the lock for "precious ignored
data"): a locked target worktree holding `db.sqlite` had it replaced by
the branch's tracked file, exit 0, no warning. The lock scopes to
removal only. A pathspec-limited probe of the push range detects it
without enumerating large ignored trees — `git -C <target-wt> ls-files
-o --ignored --exclude-standard -z -- <push_files>` names the colliding
file and stays silent about a 200-file ignored `target/` — but whether a
collision should refuse is a policy call, so it is left out.
## Verification
`wt hook pre-merge --yes` on the merged tree → exit 0, 4554 tests, no
pending snapshots. `zola build` clean, both anchors resolving and
merge.md's cross-page link landing. Every factual claim checked by
running `wt` against a purpose-built repo, and each of the three code
fixes has a test that fails without it.
> _This was written by Claude Code on behalf of Maximilian Roos_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds PR/MR review state to `wt list`'s CI status, merged into the
existing CI dot color.
## What
- New `ReviewState` (`approved` / `changes_requested` / `pending` /
`draft`) on `PrStatus`, absent when the forge reports no review signal —
branches with no review activity render exactly as before.
- **GitHub**: `reviewDecision,isDraft` join the existing single `gh pr
list --json` call (no extra API cost). Draft wins over the decision; an
empty `reviewDecision` (no required reviewers, no reviews) maps to
absent rather than `pending`, so solo repos don't show a perpetual
waiting state.
- **GitLab**: `draft` + `detailed_merge_status == "not_approved"` →
`draft`/`pending`; MR list data carries no approved/changes-requested
signal. Gitea/Azure: none.
- **Display**: review state merges into the CI dot in
`PrStatus::color()` — conflicts (yellow) > changes-requested
(**magenta**) > running (blue) > failed (red) > review-required
(**cyan**) > base CI color; draft dims like staleness. Cool colors mean
waiting (blue: on CI, cyan: on a human), warm mean act;
changes-requested outranks running because waiting can't clear it.
Magenta was the one color not already carrying a meaning in the list
row; cyan is reused from the working-tree symbols column, where the
differing glyph disambiguates.
- **JSON**: `ci.review_state` in `wt list --format=json`, vocabulary
matching Claude Code's statusline `pr.review_state` so the two surfaces
agree on names.
## Notes for review
- The merge lives in `PrStatus::color()`/`style()` — `CiStatus` stays
pure CI (cache values and JSON `status` strings unchanged). Both the
table and the statusline render through `format_indicator` → `style()`,
so there's a single chokepoint.
- Old CI cache files deserialize unchanged (the new field is an
`Option`).
- `docs/content/list.md` and `skills/worktrunk/reference/list.md` are
regenerated from `src/cli/mod.rs`. The HTML and terminal help colorizers
learned magenta/cyan (they previously mapped only the five existing
colors). Also fixed a pre-existing missing `repo_url` row in the
ci-object doc table.
- Testing: unit tables for the color merge and both forge mappings;
integration snapshots (mocked `gh`) for changes-requested /
review-required / approved / draft verifying the exact ANSI codes; the
JSON field name is pinned by an inline snapshot.
If this change is bad, it's most likely because the dot now encodes two
axes (CI health and review attention) in one color — a consumer who
reads green as "CI passed" still gets that, but red-vs-magenta now
distinguishes who rejected the branch, which is more vocabulary to
learn. The `--format=json` output keeps the axes separate for anyone who
wants their own logic.
Ref #2950
> _This was written by Claude Code on behalf of max_
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Per @max-sixty's [direction in
#2838](https://github.com/max-sixty/worktrunk/issues/2838#issuecomment-4509447593):
revert the docs portion of #2840 and keep the code. Docs continue to
recommend `pre-start`/`post-start`; both names work in code so anyone
who already followed the briefly-changed docs (e.g. @EcksDy) isn't
stranded once a release ships these aliases.
## User-visible — back to `pre-start`/`post-start`
- README, docs site, skill mirrors, `dev/*.example.toml`,
`plugins/worktrunk/README.md`, `flake.nix`, `.config/wt.toml`
- `src/cli/mod.rs` / `src/cli/config.rs` / `src/cli/step.rs` /
`src/help.rs` after_long_help and example snippets — and the auto-synced
`docs/content/` and `skills/worktrunk/reference/` mirrors
- `wt hook --help` canonical subcommand names; completion advertises
`-start` only
- `HookType` Display via strum, serde `rename`, and clap `ValueEnum`
name — all `pre-start`/`post-start`. The Rust variant identifiers stay
`PreCreate`/`PostCreate` (internal; we already paid for that rename in
#2840, and now the eventual flip is a Display-only change)
- `HooksConfig` serde canonical fields
## `*-create` still works (kept code)
- `wt hook pre-create` / `post-create` — CLI alias on the canonical
subcommand
- `pre-create` / `post-create` in config: top-level, `[hooks.*]`, and
per-project, in string, `[table]`, and `[[array-of-tables]]` form.
Mechanism: serde `alias = ...` on the field, plus a silent in-memory
rename in `migrate_content()` so the round-trip in `unknown_tree`
doesn't flag table forms as schema-unknown.
- The pre-0.32.0 `post-create` fatal-load-error machinery stays removed
— the name is reclaimed, and both forms load without error.
## Smaller bits
- `valid_user_config_keys()` / `valid_project_config_keys()` append
`pre-create` / `post-create` so the unknown-field round-trip skips them.
`test_valid_*_keys_all_deserialize` skips both aliases (they can't sit
alongside the canonical without a duplicate-field error).
- `DEPRECATED_SECTION_KEYS` drops the `pre-start`/`post-start` entries
#2840 added — `pre-start`/`post-start` are canonical again.
- `find_pre_start_from_doc` / `find_post_start_from_doc` /
`find_renamed_hook_key` / `is_non_empty_item` /
`migrate_start_hooks_doc` and their tests are removed; the migration
direction flips via a new `migrate_create_hooks_doc` (silent, mirrors
the prior shape).
- Test files `e2e_shell_post_create.rs` and `post_create_commands.rs`
rename back to `_post_start_` (via `git mv`, so the rename shows as a
rename).
## Testing
`cargo run -- hook pre-merge --yes` — 3806 tests pass; the 10 failures
are all `case_4` of `shell_wrapper::unix_tests::*` (nu-shell case; `nu`
isn't installed in this runner; same failures occur on `main`).
Also manually verified that a fresh `wt switch --create` against a
project config with `[post-create]` loads cleanly with no unknown-field
warning and the hook fires as `post-start`.
## Follow-up
Per @max-sixty: in a couple of weeks, once a release with
both-names-work is out and users have had a chance to upgrade, the docs
flip is straightforward (most of it is in `src/cli/mod.rs`'s
`after_long_help` and the doc-sync test propagates).
Re #2838.
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Phase 1 of the staged hook rename tracked in #2838: the worktree-creation hooks `pre-start`/`post-start` become `pre-create`/`post-create`. The old names keep working with no deprecation warning yet (Phase 2, months out, adds the warning).
## What changes
- `pre-create`/`post-create` are canonical everywhere: the `HookType` enum, the `HooksConfig` serde fields, the `wt hook` CLI, completion, and all docs.
- Old names keep working: `migrate_content()` rewrites `pre-start`/`post-start` config keys to `-create` before serde, and `parse_hook_type` accepts the old CLI names as silent aliases. `wt config update` rewrites them on disk; `wt config show` shows the migration diff. `wt hook <type>` execution and `wt hook show` both accept the old names; completion and `--help` advertise only the canonical names.
- `detect_deprecations()` flags the old keys so `update`/`show` act on them, but `format_deprecation_warnings()` stays silent (Phase 2 adds the warning). A new empty-warnings guard in `check_and_migrate` keeps a `-start`-only config from emitting a stray hint.
- The dead pre-0.32.0 `post-create` machinery is removed: the fatal `POST_CREATE_REMOVED_MSG` load error, the vestigial `HooksConfig.post_create` merge-fold, and `find_post_create_from_doc`. `post-create` is reclaimed as the canonical background creation hook.
## Semantic flip
Before v0.32.0, the key `post-create` named a *blocking* hook. It now names the *background* one.
Since v0.44.0, a pre-0.32.0 `post-create` config is a fatal load error on the `check_and_migrate` paths: `ProjectConfig::load` and user/system config loading, which fire on essentially every `wt` command. A repo carrying one has been unusable ever since. The one path that skips that check is `project_config_at_ref` (the base-ref read behind `wt switch --create`), which applies only structural migration. A pre-0.32.0 `post-create` surviving solely on a base ref, never checked out into a worktree, would now load as a background hook rather than folding into the blocking `pre-start`. That edge case is accepted: once `post-create` is valid again, reclaiming the name and detecting the dead key are mutually exclusive.
## Reviewing this diff
205 files, but the substance is ~36 files under `src/`. The rest is regenerated snapshots and auto-synced doc mirrors. Start with:
- `src/config/deprecation.rs` — detection (`find_renamed_hook_key`), migration (`rename_hook_key`), removal of the fatal block, the empty-warnings guard, and the `DEPRECATED_SECTION_KEYS` entries that stop unknown-field detection from flagging the migrated keys.
- `src/config/hooks.rs`, `src/git/mod.rs` — the serde field and enum renames.
- `src/config/project.rs` — `ProjectConfig::load` deserializes `check_and_migrate`'s migrated content, so a current-worktree config using the old keys loads into the canonical fields.
- `src/cli/hook.rs`, `src/commands/hook_commands.rs`, `src/completion.rs`, `src/main.rs` — the CLI alias layer; `wt hook show` accepts the old type names as hidden value-parser aliases.
- `src/cli/mod.rs` — the `wt hook` docs, including the soft-deprecation note linking #2838.
The ~93 modified snapshots also pick up deterministic env-block lines (`GIT_*: ""`, `LLVM_PROFILE_FILE`) that pre-existing snapshots already carry. That is stale-snapshot drift surfaced by the regeneration, not a behavior change.
## Testing
Full suite green (3799 tests). New coverage: `snapshot_migrate_start_to_create` (migration preserves value shape and position), `test_deprecated_start_hook_key_runs_silently` and `test_standalone_hook_start_alias_runs_silently` (old config and CLI names run with no warning), `test_config_show_displays_start_hook_migration` (`config show` reveals the diff without an "unknown field" warning), and `test_hook_show_accepts_deprecated_start_hooks` (a current-worktree config using the old keys loads, and `wt hook show` takes both the canonical and the deprecated type arguments).
Part of #2838.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The root `CLAUDE.md` loads into every conversation, so its ~510 lines
were paid on every session. This cuts it to ~190 lines without losing
any documented behavior or invariant.
Three things happened: derivable/stale content was removed (the JSON
Output Format pointer, just a redirect to `wt list --help`), prose was
tightened throughout, and task-specific detail was pushed down to the
sibling `CLAUDE.md` that loads where that work happens — the root keeps
a one-line pointer.
**Relocations** (substance preserved at the destination, root keeps a
pointer):
- Testing commands, the `mock-stub` filtered-run gotcha, Claude Code web
setup, the shell/PTY nextest SIGTTOU suspension, and codecov
investigation mechanics → `tests/CLAUDE.md`
- Doc sync taxonomy (the three categories + PRIMARY SOURCE),
three-context help-text authoring, the `.gitattributes`
`linguist-generated=false` exemption, config-TOML double-comment rule →
`docs/CLAUDE.md`
- Plugin layout, the Codex no-hooks re-enablement conditions, the
accepted `wt-switch-create` tradeoff → `plugins/worktrunk/CLAUDE.md`
(its back-pointers into the root were made self-contained so nothing
dangles)
- The add-a-CLI-command recipe → `src/commands/CLAUDE.md`
**Kept in the root, tightened but substance intact:** the governing
invariants — Data Safety, the Command Execution Principles
(`shell_exec::Cmd`, structured output, network policy, signal handling),
Project Commands Run Only After Approval (verbatim — the #2806 TOCTOU
fix depends on its wording), the codecov merge gate, Config Deprecation,
accessor naming, and the Worktree Model.
A third commit consolidates the doc-sync explanation in
`docs/CLAUDE.md`: the new "Doc sync taxonomy" section was overlapping
with the file's existing "Command documentation" area on "edit
`src/cli/mod.rs`" and `test_docs_are_in_sync`. Now the taxonomy is the
single entry point and "Command page generation" reads as the category-1
mechanism details.
Every CLAUDE.md heading quoted in a code comment is preserved verbatim
so the breadcrumbs still resolve under a case-sensitive grep: `Plugin
Layout` (`src/commands/config/codex.rs`), `Project Commands Run Only
After Approval` (`src/commands/hook_plan.rs`,
`src/commands/picker/mod.rs`), `Network Access`
(`src/git/repository/config.rs`), and `Signal Handling`
(`src/git/error.rs`, `src/commands/run_pipeline.rs`,
`src/commands/command_executor.rs`). The first push downcased the latter
two; the automated reviewer caught it and the second commit restored
Title Case across all Command Execution Principles subheadings.
`test_docs_are_in_sync` passes (docs-only change; no code touched).
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Three follow-ups from #2419 review.
- **Bare close everywhere.** With inner snapshot wrappers gone from
command pages (#2419), no `AUTO-GENERATED` markers nest inside the
help-page region anywhere in `docs/content/*.md`. The mirrored close
(`<!-- END AUTO-GENERATED from \`wt cmd --help-page\` -->`) was the only
remaining variant of the close form; collapsed to bare `MARKER_CLOSE`.
- `src/help.rs::PageMode::emit_footer()` no longer takes `subcommand`.
- The help-page regex in `readme_sync.rs` matches via non-greedy `.*?`
to bare `MARKER_CLOSE`, with a comment pointing at the new invariant
test.
- `AUTO_GENERATED_MARKER_PATTERN` strip regex built from the constants.
Added **`test_no_nested_auto_generated_markers`** — walks
`docs/content/*.md` and fails if any `AUTO-GENERATED` open ever appears
inside an already-open region. This is the explicit invariant that
bare-close pairing depends on; if a future change tries to re-introduce
nesting (e.g., restore an inner snapshot wrapper around terminal
shortcodes), the test catches it before the subtle "regex chops region
at first inner close" failure mode lands.
- **Aligned error channel.** `convert_console_blocks_in_docs` now
returns `(Vec<String>, Vec<String>)` like its sibling sync steps, with
per-file error capture for `read_dir`, dir entries, and
`read_to_string`. The caller passes errors through the same `tag()`
aggregation as everything else, so a transient I/O failure on one file
no longer aborts the whole pipeline silently. Also fixes the matching
clippy warning (`is_some_and(|e| e == \"md\")` → `is_none_or(|e| e !=
\"md\")`).
- **Docs alignment.** `docs/CLAUDE.md` updated in two places where the
prose still documented the mirrored close as the canonical form.
Visual check via curl across 12 dev-server pages confirmed no leakage of
any prior marker form. Adversarial review (subagent /popper-style)
caught the stale prose; otherwise no regressions found.
Net diff: +113/-49 (the growth is the new invariant test and explicit
per-file error handling; structural simplification shows as -49).
## Test plan
- [x] `cargo test --test integration readme_sync` — 12 sync tests pass
(was 11; +1 for the new invariant test)
- [x] `cargo test --test integration` — full integration suite (1558
tests) pass
- [x] `cargo test --test integration
test_no_nested_auto_generated_markers` — new guard test passes; manually
verified it fires on synthetic nesting
- [x] Single-pass convergence verified: `git checkout docs/ && cargo
test --test integration test_docs_are_in_sync && git diff --stat`
produces an empty diff after the first run
- [x] Visual check via local Zola dev server: 12 docs pages
(\`/worktrunk/\`, \`/llm-commits/\`, \`/claude-code/\`,
\`/tips-patterns/\`, \`/merge/\`, \`/step/\`, \`/remove/\`, \`/hook/\`,
\`/list/\`, \`/switch/\`, \`/config/\`, \`/faq/\`) — only HTML comments
contain marker text, no rendered leakage
- [x] `cargo clippy --all-targets --all-features` — clean
- [x] `cargo fmt --check` — clean
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
Two follow-ups from #2418 review.
- **Test brittleness fix.** Three previously-separate sync tests had
data dependencies that nextest could interleave:
`test_readme_examples_are_in_sync` reads `docs/content/*.md`, which
`test_docs_quickstart_examples_are_in_sync` generates from snapshots.
Under parallelism, README sync could see stale docs and produce content
that required a second run to converge. Collapsed both into the existing
`test_command_pages_and_skill_files_are_in_sync` pipeline, renamed to
`test_docs_are_in_sync`. Steps run sequentially in dependency order;
single pass converges from a clean working tree.
Each step's errors and updated-file list are tagged with the pipeline
stage (`[command pages]`, `[standalone docs]`, `[README]`, etc.) so a
failure tells a developer which stage broke without reading the test
source. README failure also now reports `(N of M section(s) updated)`.
- **Dead inner snapshot wrappers.** `expand_command_placeholders`
wrapped each terminal shortcode in command pages with `<!-- ⚠️
AUTO-GENERATED from <snap> --> ... <!-- END AUTO-GENERATED -->` markers.
Command pages regenerate wholesale from `--help-page` each sync, so the
inner wrapper served no in-place-refresh purpose — it was dead weight
nested inside the outer help-page region's markers. Stripped from
`expand_command_placeholders`'s HTML branch; net -32 lines across six
command-page docs files.
The outer help-page region's mirrored close stays — it's load-bearing
whenever any nested `AUTO-GENERATED` marker appears in the body. Comment
in `src/help.rs:emit_footer` updated to explain the role. (My follow-up
note that the mirrored close was redundant turned out to be a
misanalysis — non-greedy `.*?` only safely pairs the open with the right
close when there are no nested closes; once the inner snapshot wrappers
were gone, the mirror became technically unnecessary, but keeping it
survives any future reintroduction of nesting at no cost.)
Renamed the test in `CLAUDE.md` (4 sites) and `docs/CLAUDE.md` (2 sites)
so the documented `cargo test` invocations resolve.
Net diff: −54 lines.
## Test plan
- [x] `cargo test --test integration readme_sync` — 11 sync tests pass
(was 13; minus the two collapsed)
- [x] `cargo test --test integration` — full integration suite (1557
tests) pass
- [x] **Single-pass convergence verified**: `git checkout docs/
README.md && cargo test --test integration test_docs_are_in_sync && git
diff --stat` produces an empty diff after the first run
- [x] Visual check via local Zola dev server: \`/worktrunk/\`,
\`/llm-commits/\`, \`/claude-code/\`, \`/tips-patterns/\`, \`/merge/\`,
\`/step/\`, \`/remove/\`, \`/hook/\`, \`/list/\` all render terminal
blocks cleanly with no leaked marker strings
- [x] Adversarial review of the consolidation (subagent /popper-style):
all 4 actionable findings (stale doc references, lost README count,
missing stage tags, etc.) addressed
- [x] `cargo clippy --all-targets --all-features` — clean
- [x] `cargo fmt --check` — clean
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
Three small follow-ups + a placeholder rename, all from #2405 review
feedback.
- **Root-cause fix for `|||` corruption.**
`convert_dollar_console_to_terminal` was promoting blank lines inside
mixed `\$ cmd + output` blocks to extra `cmd=` entries, dropping the
blank from the rendered body and emitting a stray `\$` prompt (e.g.
`cmd=\"wt list|||\"`). Now distinguishes command-only blocks (blanks are
visual spacing in `cmd=`) from mixed blocks (blanks belong to body).
Removes the matching `trim_end_matches('|')` bandage in
`expand_command_placeholders`. New unit test in `src/docs.rs` covers the
mixed-block case.
- **Single-pass classification.** Folded the two filter chains over
`block_lines` into one loop with one match — fewer branches, no
duplicated predicates.
- **AUTO-GENERATED marker consolidation.** Within
`tests/integration_tests/readme_sync.rs`, the `<!-- ⚠️ AUTO-GENERATED
... -->` literal appeared in four producer / regex sites with subtly
different shapes (HTML vs plain, ID format). Factored into
`MARKER_OPEN_PREFIX` / `MARKER_OPEN_HTML_PREFIX` / `MARKER_CLOSE`
constants plus a `wrap_in_marker()` helper, and threaded those constants
into the regexes via `regex::escape`. Pure refactor — no docs/skill
files regenerated.
- **`__WT_OPEN2__` / `__WT_CLOSE2__` → `__WT_OPEN__` / `__WT_CLOSE__`.**
The `2` was meant to distinguish doubled-brace placeholders from
hypothetical single-brace ones, but Tera only treats `{{`/`}}` (not
single braces) as template delimiters — there's no second variant to
disambiguate from. Updated the producer (`src/docs.rs`), the test-side
decoder (`tests/integration_tests/readme_sync.rs`), the Zola template
(`docs/templates/shortcodes/terminal.html`), the docs-site `CLAUDE.md`,
and the regenerated `docs/content/{step,hook}.md`.
## Test plan
- [x] `cargo test --lib
docs::tests::test_convert_dollar_console_to_terminal` — exercises new
mixed-block case + existing command-only / multi-cmd / comment cases
- [x] `cargo test --test integration readme_sync` — 13 sync tests still
pass after the refactor and bandage removal
- [x] `cargo test --test integration` — full integration suite (1558
tests) pass
- [x] Visual check via local Zola dev server: `/merge/`, `/step/`,
`/remove/`, `/hook/`, `/llm-commits/`, and `/list/` all render terminal
blocks cleanly with no `|||` artifacts and no leaked placeholder strings
(`__WT_OPEN__` etc. don't appear in served HTML). The multi-command jq
recipes block on `/list/` continues to render comments as bash-styled
section headers.
- [x] `cargo clippy --all-targets --all-features` — clean
- [x] `cargo fmt --check` — clean
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Commands containing `{{ }}` template expressions (e.g., `wt step eval
'{{ branch | hash_port }}'`) were the only blocks that didn't get full
Syntect highlighting on the docs site — they fell back to accent-only
color because Tera would interpret `{{ }}` in the `cmd` parameter as
template expressions.
This uses text placeholders (`__WT_OPEN2__`, `__WT_CLOSE2__`) that pass
through Tera safely. The terminal shortcode template replaces them back
to real braces before Syntect processes them. Also fixes double-encoding
of `"` in cmd parameters (the old `"` was getting double-encoded by
Syntect to `&quot;`), using a `__WT_QUOT__` placeholder for the same
reason — Tera has no backslash-escape mechanism for string literals.
The skill file generator (`transform_docs_for_skill`) was updated to
handle the new format: extracting `cmd` parameter values and `|||`
delimiters into `$ `-prefixed bash blocks, converting legacy `<span
class="cmd">` body tags, and fixing the `[^)]*` regex that broke on `)`
inside cmd values.
Net effect: all code blocks on the docs site now have consistent
multi-color Syntect highlighting, and skill reference files have clean
`$ command` blocks instead of raw HTML.
> _This was written by Claude Code on behalf of @max-sixty_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Shell code blocks on the docs site had inconsistent syntax highlighting.
Blocks with `$ ` prompt prefixes rendered as a single flat color because
Syntect's bash grammar treats `$` as variable expansion. This PR adds `$
` prompts to all shell commands while preserving full Syntect
highlighting by routing through terminal shortcodes.
## Approach
All shell commands in `console` blocks use `$ ` prefix.
`convert_dollar_console_to_terminal()` (new library function in
`src/docs.rs`) detects `$ ` lines and emits Zola terminal shortcodes:
- **Single or multi-command blocks** (no `{{ }}`): Uses `cmd` parameter
with `|||` delimiter. The shortcode template splits, highlights each
line individually through Syntect, and wraps commands in `<span
class="cmd">` (CSS `::before` adds `$ `). Comment lines (`#`) are
highlighted as comments without a prompt.
- **Blocks with `{{ }}` template syntax**: Falls back to body approach
with `<span class="cmd">` (accent color only, since Tera would interpret
`{{ }}` in the `cmd` parameter).
The function runs in both the `--help-page` generator (CLI source →
docs) and the doc sync test (hand-written docs → terminal shortcodes).
Hand-written docs can use plain `console` fences with `$ ` and get
auto-converted.
## Key files
- `src/docs.rs` — New library module with
`convert_dollar_console_to_terminal()` and unit tests
- `docs/templates/shortcodes/terminal.html` — Template enhanced to loop
over `|||`-delimited commands, highlighting each through Syntect.
Supports self-closing `{{ }}` syntax for bodyless blocks.
- `src/help.rs` — Uses library function, updated pipeline docs
- `tests/integration_tests/readme_sync.rs` — Sync test runs conversion
on all docs (not just CLI-generated). Updated skill transformation to
handle both body and self-closing terminal shortcodes.
- All `src/cli/*.rs` — `$ ` added to all console blocks
- All `docs/content/*.md` — Auto-converted to terminal shortcodes (zero
`bash` blocks remain)
> _This was written by Claude Code on behalf of @max-sixty_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: worktrunk-bot <w@worktrunk.dev>
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Add the `[experimental]` → empty `<span>` mapping to the post-processing
table in docs CLAUDE.md, which was missing after the badge-to-heading
change in #1523.
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
\`colorize_ci_status_for_html\` was renamed to \`post_process_for_html\`
and moved from \`src/main.rs\` to \`src/help.rs\` in #1499. The docs
CLAUDE.md reference was stale.
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
The `wt select` command was moved to `wt switch` (with `wt select`
kept as a deprecated alias). The demo tape content already uses
`wt switch`, but all naming — tape filename, output name, GIF
filenames, doc references — still said wt-select. This aligns
naming with the current command structure.
Co-authored-by: Claude <noreply@anthropic.com>
Update the development workflow section to reflect that the docs dev server now starts automatically via the post-start hook, removing manual setup steps. Users can find the port using `wt list statusline` instead of manually starting the server and parsing output.
* feat: include command definition at top of doc pages
The first `///` doc comment line (the "about" / definition) now appears
at the top of each command's documentation page, before the subtitle
and after_long_help content.
Previously, this definition only appeared in the Command Reference
section at the bottom. Now the page structure is:
1. Definition (short about)
2. Subtitle (long about, if present)
3. Conceptual documentation (after_long_help)
4. Command reference
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: improve command documentation structure
- Add definition to top of web doc pages (was missing)
- Combine definition + subdefinition into single lead paragraph on web
- Remove duplication between definition and after_long_help openers
- Fix em-dash spacing (spaced per style guide)
- Fix pronoun clarity ("Creates one" vs "Creates it")
- Use indicative mood instead of second person ("For finished feature branches" not "Use when you're done")
- Add documentation guidelines to docs/CLAUDE.md
Commands updated: switch, list, select, remove, merge, step, hook, config
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: update switch help snapshot for pronoun change
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Merge test_command_pages_are_in_sync and test_skill_files_are_in_sync_with_docs
into test_command_pages_and_skill_files_are_in_sync
- Single test run now handles full chain: mod.rs → docs → skills
- Show actual file paths in error messages instead of counts
- Update CLAUDE.md documentation to reflect new test name
Co-authored-by: Claude <noreply@anthropic.com>
Consolidate 7 shell scripts into a single Taskfile.yml using go-task:
- coverage: run tests with coverage
- setup-web: setup Claude Code web environment
- fetch-assets: download assets from worktrunk-assets
- publish-assets: publish assets to worktrunk-assets
- build-social-cards: generate social card PNGs
- generate-logo: generate logo with Gemini AI
Remove update-homebrew task (now handled by CI workflow).
Update CLAUDE.md files to reference `task X` commands and add
task runner installation instructions for web environments.
Co-authored-by: Claude <noreply@anthropic.com>
* Redesign social cards with horizontal layout
- New horizontal layout: logo left, text right
- Warm radial glow emanating from logo
- Refined typography: 88px title with tight tracking, 28px tagline
- Darker title color (#1a1817) for better hierarchy
- Move generated PNGs to assets repo (same pattern as demos)
- Add build script with automatic font downloading
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix deploy workflow to include social cards
The assets repo now has both demos/ and social/ directories.
Co-Authored-By: Claude <noreply@anthropic.com>
* Simplify assets structure: single path copy
Restructured worktrunk-assets repo to have all content under assets/
so deploy workflow can use a single `cp -r` command.
Co-Authored-By: Claude <noreply@anthropic.com>
* Address review feedback
- Fix typo: "ONGs" → "PNGs"
- Fix stale comment: "Google Fonts" → "GitHub releases"
- Add -f flag to curl for better error handling
- Document purpose of each social card size
Co-Authored-By: Claude <noreply@anthropic.com>
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Fix asset URL in README for restructured repo
The worktrunk-assets repo was restructured from demos/ to assets/.
Update the URL transform in readme_sync.rs to match.
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix README demo links after assets repo restructure
- Update wt-core.gif URL from demos/ to assets/ path
- Add PNGs exception to typos config (was incorrectly flagged)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Ensures template examples in docs work as described. Tests cover basic
variables, filters (sanitize, hash_port), operator precedence with
concatenation, and full command examples from docs.
Catches issues like PR #373 where operator precedence was documented
incorrectly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Restructure demo asset output to use mode (docs/social) and theme (light/dark)
subdirectories. Updates all asset references in documentation, build scripts,
and publish tooling to match the new layout:
- docs/light/ and docs/dark/ for documentation site demos (1600x900)
- social/light/ for social media demos (1200x700)
Simplifies asset management and allows docs/social builds to coexist without
overwriting each other. Updates build script to organize GIFs by theme after
recording, fetch-assets to preserve directory structure, and all markdown
references to use the new paths. Renames twitter build target to social for
broader applicability.
- Move demo GIF output directory from docs/demos/out/ to docs/static/assets/
- Update build and publish scripts to use consolidated assets location
- Update .gitignore to reflect new assets path
- Document unified workflow for building and fetching demo assets
* Update docs and help text to clarify default branch references
* Replace "main" with "default branch" in docs and help text
Update references throughout documentation and help snapshots to use
"default branch" instead of hardcoded "main" for clarity. Changes include:
- Help text descriptions for list, merge, switch, and remove commands
- Configuration file documentation and examples
- JSON output field descriptions
- Status symbol explanations
- Example commands and shortcuts
Also update environment variable from GIT_EDITOR to GIT_CONFIG_GLOBAL
in test snapshots and add missing RUST_LOG variable.
* Move backslash normalization to start of filter chain (#263)
By normalizing backslashes to forward slashes FIRST, all subsequent
path filters only need the forward-slash version. This removes:
- Duplicate path filters for backslash versions
- The later backslash normalization filter
Simplifies the filter chain by having one canonical path format.
Co-authored-by: Claude <noreply@anthropic.com>
* Combine ~/repo pattern with optional worktree suffix (#264)
Use a single pattern with optional capture group instead of two separate
patterns. The optional suffix (\.[a-zA-Z0-9_-]+)? matches worktree paths
like ~/repo.feature while also matching plain ~/repo.
Co-authored-by: Claude <noreply@anthropic.com>
* Inline symbol literals in formatted messages
Replace symbol constant references with their literal characters inside
cformat! color blocks. This ensures symbols render with proper coloring
instead of appearing outside the colored sections.
* Add demo-simple scaffold with shared fixtures and library
Extract common demo setup logic into reusable lib.py functions:
- prepare_base_repo() for git repo, Rust project, mock CLIs
- prepare_demo_repo() for full rich repo with varied branches
- commit_dated() for dated commits with offsets
- Helper functions for creating alpha/beta/hooks branches
Create shared fixtures directory:
- lib.rs and lib-hooks.rs for Rust project variants
- gh-mock.sh for mocked GitHub CLI with per-branch CI status
- alpha-readme.md for large diff demo content
- starship.toml for consistent shell prompt
Add demo-simple demo scaffold:
- Build script and VHS tape for simple workflow demo
- Shows hooks execution, branch creation, and removal
- Uses shared fixtures and library for setup
Update wt demo to use shared library, reducing duplication
by ~190 lines while maintaining identical repo structure.
* Refactor demo infrastructure: consolidate shared code and rename demos
- Create shared/ Python package with unified imports
- Move lib.py and themes.py into shared/
- Add __init__.py that re-exports all utilities
- Move fixtures into shared/fixtures/
- Rename demos for clarity
- demo-simple → wt-core (core workflow demo)
- wt → wt-merge (merge-focused demo)
- Update doc references
- Homepage and README use wt-core.gif
- merge.md page uses wt-merge.gif
- Simplify gitignore
- Use single glob pattern docs/demos/*/out/
- Remove per-demo .gitignore files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add merge.md to lychee exclude_path
The merge.md page now has root-relative asset paths (/assets/wt-merge.gif)
that lychee can't resolve locally. These assets are fetched at deploy time.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add wt-merge demo GIF to merge command help
Add demo placeholder in cli.rs so the GIF appears in generated docs.
The placeholder expands to an HTML figure with light/dark variants.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Update help snapshots for merge demo placeholder
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Consolidate all development scripts in dev/:
- fetch-assets: downloads demo GIFs for local docs development
- publish-assets: publishes demo GIFs to worktrunk-assets repo
Update references in .config/wt.toml and docs/CLAUDE.md.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Make it explicit that the vhs-keystrokes binary must be built before
regenerating demos, and add a check command to verify it exists.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add documentation for building the custom VHS fork required by the
wt-select demo. Update .gitignore to exclude the locally-built
vhs-keystrokes binary.
* feat(docs): Add dark theme support for demo GIFs
Demo GIFs now automatically switch between light and dark variants based on
system color scheme preference using the <picture> element with media queries.
Changes:
- Add themes.py with light/dark VHS theme definitions matching doc site CSS
- Parameterize demo.tape files to accept theme variable
- Update build scripts to generate both light and dark GIF variants
- Update markdown to use <picture> element for automatic theme switching
- Update publish-assets to include dark variants
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix CI: generate picture element from demo placeholder
The <picture> element for light/dark theme switching should be generated
by expand_demo_placeholders() in main.rs, not manually edited in the
markdown. This ensures the AUTO-GENERATED region stays in sync.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Implements a system to include subcommand help as H2 sections within parent command docs pages using HTML comment placeholders.
In `cli.rs`, add `<!-- subdoc: subcommand-name -->` placeholders to a command's `after_long_help`. During `--help-page` generation, these expand to formatted sections with the subcommand's documentation and command reference.
The expansion process:
- Locates `<!-- subdoc: create -->` style placeholders
- Fetches the subcommand's help output via `get_help_reference()`
- Increases markdown heading levels (## → ###) so nested headings are children of the main H2
- Combines the subcommand's conceptual docs with a "Command reference" section
- Strips after_long_help from the help reference to avoid duplication
Also refactors help reference extraction into a reusable `get_help_reference()` function and adds `increase_heading_levels()` for proper nesting. Adds documentation in `CLAUDE.md` and expands `config.md` with full template content for `wt config create` and `wt config var` subcommands.
Command pages now preserve frontmatter in skeleton files (title, weight, group)
and replace only the AUTO-GENERATED marker region during sync. The END tag
mirrors the source ID to unambiguously match regions even with nested markers.
This allows manual editing of frontmatter and conceptual docs while keeping
auto-generated content synchronized. The main.rs generator outputs just the
content between START and END markers, and the sync test uses regex to find
and replace the exact region.
Custom "warm workbench" theme now self-contained without Juice inheritance. Eliminates theme extension complexity and provides complete control over documentation site design.
Key changes:
- Removed docs/themes/juice submodule and all Juice template inheritance
- Rewrote base.html as standalone template with full HTML structure
- Migrated all Juice CSS variables and utilities into custom.scss
- Updated config.toml to remove theme reference and rename juice_* config keys
- Consolidated template structure: page.html and index.html now extend base.html directly
- Removed lychee exclusion for Juice theme README (no longer applicable)
- Moved normalize.css from theme to docs/static for direct control
The theme is now fully self-hosted with no external dependencies, making it easier to maintain and extend.
The zola serve command now uses `-p 0` to automatically pick an available
port instead of hardcoding 1111. Updated documentation to reflect that the
port is dynamic and should be checked when the server starts.
Details now live in base.html next to the JS fix.
CLAUDE.md just references it. Fixed outdated comments
that incorrectly claimed lvh was stable on Firefox iOS.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a detailed section explaining:
- The root cause (missing WebKit APIs in Firefox/Chrome iOS)
- Our current JS workaround
- What we tried that didn't work
- Our preference for a simpler solution
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove scaffolding warnings and demo GIF workflow details from top section. Consolidate development guidance with clearer structure: dev server setup, verification strategies with Playwright guidance, theme architecture with iOS viewport polyfill note, and command generation documentation.
Move demo asset workflow to end with updated script paths reflecting repository reorganization.
Add fetch-assets script to download demos from worktrunk-assets repo during local development, replacing static GIF checked into this repo. Update lychee config to skip localhost URLs during CI link checks. Remove wt-demo.gif and add docs/static/assets/ to gitignore since assets are now fetched at dev time.
Updates documentation site navigation and homepage to reflect the new page name, replacing all references from "quickstart" to "why-worktrunk" across configuration, templates, and documentation.
Text-only changes no longer require Playwright verification — users can review rendered content directly. Visual changes (CSS, layout, templates) still need verification to catch hidden regressions in specificity, inheritance, and responsive behavior.
Restructured guidance to distinguish between the two workflows and simplified language around common visual issues to check.
* Standardize doc headings to sentence case
Use sentence case for all documentation headings, matching modern
technical documentation conventions (GitHub, Stripe, Tailwind).
Exceptions preserved:
- Proper nouns: Claude Code, Worktrunk, Node.js, Python, Rust
- Acronyms: LLM, CI, JSON, API, CLI
- Code/commands: wt merge, post-create (stay lowercase)
- Questions: "Why Worktrunk?" (natural casing)
Changes span both manual docs and CLI help text (via cli.rs), with
auto-generated command pages regenerated to match.
* Update README and test snapshots for sentence case headings
Update auto-generated README sections and test snapshots to match
the sentence case heading changes in the previous commit.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Increase PTY drain wait to fix flaky Ubuntu CI test
The test_list_progressive_many_worktrees test was failing intermittently
on Ubuntu CI because 500ms wasn't enough time for the kernel to flush
all PTY buffer data under load. Increased MIN_DRAIN_WAIT_MS to 1000ms
to provide more margin for slow CI runners with many worktrees.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Add realistic example output for `wt list`, `wt list --full`, and
`wt list --branches` to the documentation. Examples are generated from
integration test snapshots and automatically updated when snapshots change.
- Update docs/content/list.md with terminal shortcodes showing actual command output
- Add helper functions for running `--full` and `--branches` variants from specific directories
- Create new test cases for each example variant with realistic worktree states
- Implement command placeholder expansion in readme_sync.rs to replace `wt list`
commands with terminal output blocks
- Add CSS classes (.d, .g, .r, .c) for ANSI color styling in documentation
- Rename simple_list snapshot to list for clarity
- Refactor snapshot name matching and content processing for maintainability