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
- 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>
* 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>
- 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
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.
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.
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.
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.
Remove setup_gh_mock, build_shell_env, clean_ansi_output, and
run_fish_script from the shared demo library along with their exports
from __init__.py. These functions are no longer used by any demo
recording scripts.
Add demo configuration files, mock CLI binaries, and recorded GIF
demonstrations for worktrunk-core showcase. Includes starship prompt
config, git repository structure, and tool mocks for cargo, npm, docker,
gh, and llm. Also updates theme colors and removes unnecessary asset
copying logic.
- 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
Add wt-zellij-omnibus demo to docs build target and update demo
documentation. Fix race condition in background worktree removal by
adding delay before directory deletion to prevent "shell-init: error
retrieving current directory" when shell attempts to read cwd after
process exit. Update Fish shell configuration to disable cursor
blinking for cleaner demo recordings.
- Add wt-zellij-omnibus demo to docs site showing advanced features
(multiple Claude agents, hooks, LLM commits, merge workflow)
- Fix shell-init error after wt remove/merge by adding 1s delay before
background removal, giving shell wrapper time to cd away
- Add cursor blink disable escape sequences to fish config for cleaner
demo recordings
- Simplify dependency check to always require zellij
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Pass repo_root to setup_fish_config and call `wt config shell install`
to properly install the shell extension and completions instead of
sourcing the init output directly in the config file.
Use prepare_demo_repo() with alpha/beta/hooks branches instead of custom
streaming/doctor/llm-templates setup. This makes all three demos (wt-core,
wt-merge, wt-select) use the same base repository.
- Add utils.rs fixture to alpha branch for larger committed diff (+272 lines)
- Update demo.tape to navigate alpha and filter with "alp"
- Simplify CLAUDE.md to reflect unified approach
- Remove unused create_branches parameter from prepare_demo_repo()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Add clean_ansi_output and run_fish_script helper functions to shared
lib.py to eliminate duplication in wt-core and wt-merge build scripts.
Update both scripts to use the new helpers instead of inline subprocess
and regex calls.
* 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>