The CI column in `wt list --full` (and the statusline) previously showed
a single colored dot. It now shows the branch's open PR/MR reference —
`#3035` on GitHub/Gitea/Azure DevOps, `!3035` on GitLab — colored by CI
status, dimmed when stale, and hyperlinked to the PR. When no number is
available (branch workflows without a PR/MR, pre-number cache entries,
or a number wider than the allocated column), the cell shows a bare `#`
in the same colors. Fetch errors always render `⚠`, even when a number
is known — Error and Conflicts share yellow, so a yellow `#3035` would
read as a conflicted PR.
The branch merges main's review-state feature (#3044): review colors
(magenta/cyan) and draft dimming apply to the number cells exactly as
they did to the dot, and the `--help` legend shows colored `#` samples
for all seven states (the interim version had dropped the colored
samples from the legend entirely).
## The width problem
`wt list` renders skeleton-first: column widths are fixed before any CI
data arrives, and the table never resizes mid-render. The PR number's
width therefore has to be known up front. The solution is a repo-level
ratchet cache (`.git/wt/cache/pr-number/max.json`) holding the largest
PR number any fetch has seen — PR numbers are monotonic per repo, so the
value needs no invalidation. Pre-skeleton, `collect` reads that one file
and sizes the column exactly; on a cold cache the estimate is 5 chars
(`#9999`). A number that outgrows the estimate renders as the bare `#`
for that run and sizes correctly on the next run once the ratchet
records it. The ratchet is deliberately separate from the per-branch
`ci-status/` entries so the width hint isn't coupled to branch-entry
retention, and `detect` re-ratchets on cache hits too, so a deleted or
racily regressed `max.json` heals from locally cached numbers instead of
waiting out the TTL.
## Reviewer's map
- `src/commands/list/ci_status/mod.rs` — `PrRef` (number + forge sigil,
`PrRef::pr`/`PrRef::mr` constructors), `PrStatus.number` (serde-default
so pre-existing cache entries still deserialize, rendering `#` until
their 30–60s TTL expires), `format_cell` width-aware renderer with the
Error guard, ratchet in `detect` (both cache-hit and fetch paths)
- `src/commands/list/ci_status/cache.rs` — `MaxPrNumber` ratchet
(read/ratchet/clear)
- `src/commands/list/ci_status/{github,gitlab,gitea,azure}.rs` — each
fetcher populates the number (`gh --json number`, `iid`, Gitea `number`,
`pullRequestId`); GitLab's mr-view-failure path carries the
iid/URL/review state into the error status so the `⚠` stays clickable
- `src/commands/list/layout.rs`, `collect/mod.rs` — width estimate
threading
- `src/commands/list/render.rs`, `model/item.rs` — table cell and
statusline both go through `format_cell`
- `src/commands/list/json_output.rs` — `ci.number` field
- `src/commands/config/state.rs` — ratchet shown by `state get`/`cache
get` (table + JSON) and swept with the CI cache category, including the
deprecated `ci-status clear --all` path
- `src/md_help.rs`, `src/help.rs` — legend colorization rules rewritten
from `●` to `#` (terminal + website)
Most of the diff is snapshot churn from the column width and glyph
changes plus regenerated docs mirrors.
Known trade-offs: concurrent statusline ratchet writes can transiently
lose an update (monotonic, re-learns on the next render, documented at
the write site); one anomalously high PR number widens the column until
`wt config state cache clear`; an open Azure DevOps PR still shows gray
`NoCI` instead of its pipeline status — a pre-existing gap, now marked
`TODO(azure-pr-pipeline)`.
Testing: unit tests for `format_cell` (including the Error-with-number
and oversized-number link cases)/`pr_ref_width`/ratchet/width estimates;
integration coverage for all four forges with real numbers (the Gitea
mocks now exercise the number path too), review-state × number
composition, the GitLab mr-view-failure `⚠` and branch-pipeline success
paths, cache-TTL expiry → refetch, the statusline number view, and the
`wt config state` surfaces.
> _This was written by Claude Code on behalf of max_
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Follow-up to #2979. The shared demo tape set `Env CLAUDECODE ""` so that
a `claude` invoked while recording would not be rejected as a nested
session. Claude Code no longer blocks nested `claude -p`, and #2979
dropped the matching workaround in commit generation, so the override is
unneeded. It is not part of the recorded output, so the GIFs are
unchanged; no re-record.
> _This was written by Claude Code on behalf of max_
Co-authored-by: Claude Opus 4.8 (1M context) <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)
Single ffmpeg call per checkpoint instead of ~20 separate calls. Builds
a `select` filter expression for the full frame range and extracts all
sampled frames in one pass with `-vsync vfr`. OCR still short-circuits
on first match.
> _This was written by Claude Code on behalf of maximilian_
Co-authored-by: Claude <noreply@anthropic.com>
OCR validation checked a single exact frame number, which broke whenever
Claude Code's UI shifted timing slightly. Now uses a `Checkpoint`
dataclass with frame ranges — scans every 10th frame across a ~200-frame
window and passes if any frame matches. Resilient to normal timing
shifts without losing validation rigor.
Build script now automatically runs TUI demos sequentially (Zellij
conflicts when parallel were the root cause of previous failures), while
non-TUI demos continue running in parallel. Adds per-demo and per-target
timing summaries.
> _This was written by Claude Code on behalf of maximilian_
Co-authored-by: Claude <noreply@anthropic.com>
Three improvements to demo GIF recordings:
- **Fish syntax highlighting** — commands render in amber matching the
website's `.cmd` accent. Params/args use the default foreground.
Starship prompt uses craft-brown (`#8b7355`) to differentiate from
commands. Two delivery mechanisms: `fish_variables` for
`--shell`/`--snapshot` mode, `colors.fish` sourced in VHS hidden section
(VHS applies `Env HOME` after fish starts).
- **Delta diff viewer** — `GIT_PAGER` set per-theme (`delta
--paging=never --light` for light, `delta --paging=never` for dark).
Empty for text/snapshot recordings.
- **Light theme aligned to website** — terminal colors updated to match
`_variables.html`. Cyan and green desaturated to avoid garish bold
rendering.
Also: build script accepts multiple targets (`./build docs social`),
kills stale Zellij processes before recording, and requires delta only
for GIF mode.
> _This was written by Claude Code on behalf of maximilian_
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Add early `check_ffmpeg_libass()` at demo build startup — exits with
install
instructions when ffmpeg lacks libass (required for keystroke overlay
ASS
subtitles)
- Add output verification in `record_vhs()` — catches silent failures
where VHS
exits 0 but produces no GIF
- Document the Homebrew API-bottle vs tap-formula issue in demos
CLAUDE.md
Homebrew's API-sourced formula strips `libass`; the tap formula includes
it.
The fix is `HOMEBREW_NO_INSTALL_FROM_API=1 brew install
--build-from-source ffmpeg`.
## Test plan
- [x] Verified `check_ffmpeg_libass()` passes with correct ffmpeg
- [x] Verified full demo build succeeds with `--only wt-switch-picker`
- [x] Lints pass (`pre-commit run --all-files`)
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Add `IS_DEMO=1` to `shared-commands.tape` so all demo recordings hide
email/org from the Claude Code UI
## Test plan
- [ ] Rebuild a demo that launches Claude Code (e.g., `wt-switch`) and
verify no account info in header
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Remove stale "Approved hook commands" subsection from config docs —
approvals moved to `approvals.toml` and no longer belong in the user
config section
- Fix version regex filters in test infra that failed to handle `-dirty`
suffix on bare hash versions (when `git describe` has no reachable
tags), making snapshots non-deterministic
- Add `commands.jsonl` documentation to `wt config state logs` help text
- Add "Documentation accuracy" checklist to PR review skill
- Remove deleted `test_clone_bare_repo_list_no_status_errors` (covered
by #1112's `BareRepoTest`)
- Minor cleanup: remove "select" from CLAUDE.md command list, remove
stale pre-release-replacements, update skill version marker
## Test plan
- [x] All 1084 tests pass locally
- [x] Pre-commit lints pass
- [x] Snapshot tests reviewed — version snap now shows `wt [VERSION]`
(not `wt [VERSION]-dirty`), config help snaps reflect removed approvals
section
- [x] Doc sync test passes
(`test_command_pages_and_skill_files_are_in_sync`)
> _This was written by Claude Code on behalf of @max-sixty_
---------
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Remove the omnibus-specific LLM mock override from `build`
- Use the shared mock from `lib.py` for all demos (same commit message,
same summary branches)
- Net -27 lines
## Test plan
- [ ] `./docs/demos/build docs --only wt-zellij-omnibus --text` builds
without error
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- LLM mock handles both commit messages and summary generation via stdin
branching
- `_write_user_config` defaults to `commit_generation=True` and always
writes `[list] summary = true`
- All demos showing `wt list --full` (wt-core, wt-list,
wt-zellij-omnibus) now display the Summary column
## Test plan
- [ ] `./docs/demos/build docs --only wt-list` — verify Summary column
in `wt list --full`
- [ ] `./docs/demos/build docs --only wt-core` — same
- [ ] Demos without `wt list --full` unaffected (extra config is
harmless)
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Replace `Ctrl+t`/`Ctrl+n` with Zellij session manager sequences
(`Ctrl+Space` → `tn`/`on`) to avoid Claude Code TUI intercepting
keystrokes
- Remove initial `wt list` — demo now starts directly with the
interactive picker
- Increase marketplace install sleep (5s → 10s) for reliability
- Suppress LSP plugin recommendation dialog via
`lspRecommendationDisabled`
- Update OCR validation checkpoints for new frame timing
## Test plan
- [x] Demo builds successfully (`./docs/demos/build docs --only
wt-zellij-omnibus`)
- [x] OCR validation passes (both frame checkpoints)
- [x] Visual review of key frames confirms no errors
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Add interactive picker and copy build caches to home page "Workflow
automation" section
- Update omnibus demo to showcase the picker (TAB 4 uses `wt switch`
with no args, filters to "au", selects auth)
- Fix demo infrastructure: `CLAUDECODE=""` for nested sessions, model →
Opus 4.6, approvals.toml migration, comprehensive tip suppression
## Test plan
- [x] Demo builds and validates (`./docs/demos/build docs --only
wt-zellij-omnibus`)
- [x] Assets published to worktrunk-assets repo
- [x] Doc sync test passes
- [ ] CI green
> _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>
* feat(switch): integrate interactive picker into `wt switch` (#890)
`wt switch` without arguments now opens the interactive picker (previously
`wt select`). This simplifies the mental model: one command for all switching.
- `wt switch` → opens interactive picker (Unix) or shows error (Windows)
- `wt switch --branches/--remotes` → customizes picker
- `wt select` → deprecated hidden alias with warning
Removes the separate `wt select` docs page since the functionality is now
documented under `wt switch`. The `[select]` config section is preserved
for backward compatibility with a TODO to rename once migration is confirmed.
Closes#890
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(test): make switch TTY test Unix-only
The test expects the Unix error message ("Interactive picker requires
an interactive terminal") but Windows shows a different message
("Interactive picker is not available on Windows").
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: trigger CI
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(switch): require branch when using --create, --base, --execute, or --clobber
These flags only make sense when switching to a specific branch, not when
opening the interactive picker. Previously, running `wt switch --create`
would silently ignore the flag and open the picker instead.
Now clap properly enforces that these flags require a branch argument:
$ wt switch --create
error: the following required arguments were not provided:
<BRANCH>
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(test): update snapshot for interactive picker error message
The snapshot was stale after integrating `wt select` into `wt switch`.
Now `wt switch` without args shows the picker error, not clap's missing
argument message.
Co-Authored-By: Claude <noreply@anthropic.com>
* docs(switch): move interactive picker section after shortcuts
The picker is a navigation method (like shortcuts), not an afterthought.
New order: Shortcuts → Interactive picker → GitHub/GitLab PRs → Troubleshooting
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(test): update help snapshot after doc reorg
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(output): unify background hook output into single line
Combine contiguous post-switch and post-start hooks into a single
output line when both are present (common in `wt switch --create`).
Old format (2 lines):
Running post-switch hooks @ ~/repo.feature: user:foo
Running post-start hooks @ ~/repo.feature: project:bar
New format (1 line):
Running post-switch: user:foo; post-start: project:bar @ ~/repo.feature
Changes:
- Rename spawn_hook_commands_background → spawn_background_hooks
- Add prepare_background_hooks() to collect hooks before spawning
- Add group_commands_by_hook_type() to group commands by type
- Update handle_switch.rs to batch hooks from both types
- Remove unused spawn_post_start_commands/spawn_post_switch_commands
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: address review feedback
- Update doc comments in global.rs to use new API functions
- Update demo snapshot to use new output format
- Make prepare_background_hooks pub(crate) (internal only)
Co-Authored-By: Claude <noreply@anthropic.com>
* test: add coverage for multiple unnamed hooks of same type
Add test_user_and_project_unnamed_post_start to exercise the unnamed
index tracking code when both user and project have unnamed hooks for
the same hook type (post-start).
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Add OCR-based validation for TUI demos
TUI demos (Zellij, interactive UIs) can't be validated via text snapshots because
VHS only captures the outer terminal, not content inside multiplexers. Instead,
extract key frames from GIFs and use OCR (tesseract) to verify expected patterns.
Add `docs/demos/shared/validation.py` with checkpoint definitions and validation
logic. Update build script to validate TUI demos with defined checkpoints during
snapshot mode, skipping those without checkpoints. Document validation approach
in CLAUDE.md with requirements and checkpoint definitions for wt-zellij-omnibus.
* fix(demos): pre-populate Zellij permissions cache to avoid dialog
The zellij-tab-name plugin shows a permission dialog on first run,
which was appearing in recorded demos. Fix by pre-populating the
permissions.kdl cache file before Zellij starts.
Also simplifies the tape by removing the "y" keypress and clear
that were working around the permission dialog.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(demos): re-enable OCR validation checkpoints, handle permission dialog race
The permission dialog timing is non-deterministic (depends on macOS cache state).
Handle both cases:
- If dialog appears: "y" dismisses it
- If no dialog (cache hit): "y" + Enter + clear cleans up the shell
Re-enables validation checkpoints with calibrated frame numbers:
- Frame 100: wt list output with branch table
- Frame 500: Claude UI with Opus indicator
- Frame 2000: Final wt list --full output
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(statusline): add --format=json and --format=claude-code options
Add `--format=json` to output current worktree as JSON (same structure as
`wt list --format=json` but single-element array).
Migrate `--claude-code` to `--format=claude-code` as the canonical syntax.
The old `--claude-code` flag is hidden for backwards compatibility (no
deprecation warning).
Also fixes nested worktree detection: previously used `starts_with()` prefix
matching which would incorrectly identify the parent worktree. Now uses
`git rev-parse --show-toplevel` via `repo.worktree_at().root()` with path
canonicalization, matching the approach in `wt list`.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(statusline): require current_dir in Claude Code JSON
When parsing Claude Code JSON context, treat missing `.workspace.current_dir`
as invalid input (return None). The caller already falls back to
`env::current_dir()` when no valid context is parsed.
This is cleaner than fabricating a current_dir value - if Claude Code sends
JSON, it should include the required fields.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Add `--snapshot` flag to the demo build script that extracts wt/git
commands from VHS tapes and captures their output for regression testing.
This catches unexpected changes like new hints or warnings appearing
in demos, which would otherwise only be noticed by visually inspecting
GIFs.
Key features:
- Extracts commands from tape files (only after Show directive)
- Runs them in the demo environment and captures stdout/stderr
- Normalizes temp paths to <DEMO_DIR> for stable diffs
- Skips TUI demos (wt-select, wt-switch, etc.) automatically
- Documents expected vs regression-indicating changes
Usage: ./docs/demos/build docs --snapshot
Co-authored-by: Claude <noreply@anthropic.com>
Demo changes:
- Suppress worktree-path hint by pre-marking it as shown in git config
- Fix parallel download race condition with PID-unique temp files
The "To customize worktree locations..." hint was appearing in demos,
adding visual noise. Pre-mark the hint as shown during demo setup.
Also fixes a race condition when multiple demo builds run in parallel -
the Claude binary download could fail if two processes tried to rename
to the same destination simultaneously.
Co-authored-by: Claude <noreply@anthropic.com>
- Update deprecated [commit-generation] config format to [commit.generation]
with single command string (per f8aaead5b)
- Add context window gauge (moon phase 🌕→🌑) to statusline docs
- Add remote-only branch CI status detection note for --remotes flag
- Update demo build script to use new config format
- Sync auto-generated docs and skill references
Co-authored-by: Claude <noreply@anthropic.com>
Require manual publish when asset files are deleted to prevent
accidental removals. Refactor demo build to only copy starship config
for docs target and simplify setup_demo_output usage.
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>
- Add copy-ignored and dev server hooks (both as post-start for background execution)
- Use {{ branch | hash_port }} template for dev server port
- Add .env file so copy-ignored has something to demonstrate
- Use -- 'Add settings page' to showcase passing instructions to Claude
- Reduce pre-enter pause from 1s to 500ms for snappier feel
Co-authored-by: Claude <noreply@anthropic.com>
- Move `clear` before `Show` in omnibus tape (was typing "clear" on screen)
- Increase sleep after `clear` from 500ms to 1s in all tapes
- Prevents first frame from showing setup commands/SSH messages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add pre-remove hook to .config/wt.toml that kills the zola dev server
when a worktree is removed (prevents orphaned processes)
- Add zellij quit command to demo tape after recording stops
- Document the pre-remove cleanup pattern in tips-patterns.md dev server example
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
- Move VHS fork from manual setup to automatic build from source
- Download Claude Code binary and Zellij plugin on demand
- Remove hard-coded binary paths and vhs-keystrokes detection
- Simplify demo registration by removing vhs_type parameter
- Update documentation with new dependency management approach
- Requires Go for building VHS fork, automatically validates on first run
The `git diff` command was splitting across panes because the sleep
after `Ctrl+n` (new pane) was too short (600ms). Increased to 1.2s.
Also added GIF review documentation to help catch similar issues.
Co-authored-by: Claude <noreply@anthropic.com>
- Merge wt-select-short.tape into wt-select.tape for single unified demo
- Update docs and build config to use wt-select for both docs and social
- Adjust timing and interactions in unified tape
- Fix wt-zellij-omnibus demo npm dev server backgrounding
Fix the npm dev server command to remove the background ampersand to
match hook configuration, and update URL column snapshots to apply dim
formatting consistently across test outputs.
Remove is_interactive_tape function and simplify hooks config
generation in prepare_with_hooks by using list concatenation instead
of f-string formatting.
- Reformat imports and function arguments for better readability
- Consolidate multi-line imports into organized lists
- Wrap long function signatures and calls
- Normalize string quotes and spacing in configuration files
- Improve code style consistency across build, lib.py, and themes.py
Change bin directory from ~/bin to ~/.local/bin for mock CLIs (npm, llm,
gh, cat) and update PATH references in build script and tape files to
match the standard XDG Base Directory specification.
Document the requirement to set ANTHROPIC_API_KEY environment variable for demos that launch Claude Code, and explain that API credits are consumed during recording.
Remove the trailing `&` from npm dev commands in devserver and
zellij_omnibus demo configurations. The ampersand was causing
processes to run in the background unnecessarily.
* docs: add demo GIFs to command pages
Add demos to switch, list, merge, llm-commits, and claude-code pages.
Position demos after the intro sentence for consistent layout.
Changes:
- Add wt-switch, wt-list, wt-commit, wt-statusline to DOCS_DEMOS
- Update doc pages with <figure class="demo"> elements
- Update CLAUDE.md with new available demos list
Note: Demos involving `claude` (wt-switch, wt-statusline) need fixes
to the Claude Code mock before the GIFs look correct.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: add demo markers to cli.rs for switch, list, merge commands
Demo markers in after_long_help allow demos to persist through page
regeneration by test_command_pages_are_in_sync.
Positioned after intro sentence (matching wt-select pattern).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: add new demo pages to lychee exclude_path
These files have root-relative asset paths that lychee can't resolve
locally (assets are fetched at deploy time from worktrunk-assets repo).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: update help snapshots for demo markers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: exclude asset GIF links by pattern instead of by file
Broadened `/assets/wt-.*\.gif` to `/assets/.*\.gif` to match all asset
directories including `/assets/docs/{light,dark}/`. This is cleaner than
excluding entire files from link checking.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: pin lychee to 0.20.x in CI
Version 0.22.0 has a regression where exclude patterns for root-relative
paths (like /assets/*.gif) don't work properly - they're treated as
errors instead of being excluded.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Remove duplicate Require statements from tape files by sourcing
shared-commands.tape after shared-setup.tape. Consolidate multiple
preparation functions into parameterized versions using functools.partial
to reduce code duplication in demo registrations.
Move repeated VHS configuration and environment setup from individual tape
files into two shared sources:
- shared-setup.tape: VHS Set directives (Width, Height, Theme, etc)
- shared-commands.tape: Environment variables and shell initialization
Update render_tape() to inline Source directives since VHS doesn't support
them natively. This reduces duplication across 11 tape files by ~330 lines
while keeping tape logic readable.
Add concurrent.futures ProcessPoolExecutor to record multiple demos in
parallel. Introduce record_demo() function to encapsulate single demo
recording logic for subprocess compatibility. Replace lambda with named
function prepare_with_hooks_deps() for multiprocessing pickling. Add
--sequential flag to opt out of parallel execution. Improve output
formatting and error handling with failed demo tracking.
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.
Update wt-list-remove and wt-zellij-omnibus tapes to demonstrate
tab completion functionality. Add fish completion sourcing and
replace direct command typing with tab-completion sequences showing
menu selection, review time, and command execution flow.
Create shared-setup.tape with common configuration (shell settings,
env vars, fish initialization) and update all 12 demo tapes to Source
it instead of duplicating the setup. Each tape can still customize
pre-show commands for demo-specific needs like claude plugin install.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add tab completion support to the wt-core demo by installing fish
completions during base repo setup and pre-loading them in the demo
shell config. Update demo tape to showcase tab cycling through branch
options when switching worktrees. Simplify setup by removing redundant
repo_root parameter from fish config function. Add timing guidelines
for tab completion sequences to maintain natural pacing.
Support recording text output from demo tapes via VHS native .txt format
for capturing authentic shell sessions. Add --text flag to build script to
record shell output instead of GIFs, with automatic detection and skipping
of interactive demos. Refactor tape rendering to share template variable
replacements between GIF and text recording modes.
Update documentation, tests, and snapshots to use the new `--yes` flag
instead of `--force`. The `--yes` flag is used to bypass approval prompts
and confirmations, while `--force` is now reserved for destructive
operations like forced file removal.