worktrunk 0.45.2

A CLI for Git worktree management, designed for parallel AI agent workflows
Documentation
# Docs Site

This is the Zola-based documentation site for Worktrunk, published at worktrunk.dev.

## Development workflow

The docs dev server starts automatically via the post-start hook. Find the port with `wt list statusline`.

For static builds with local URLs (e.g., testing with a simple HTTP server):

```bash
zola build --base-url "http://127.0.0.1:PORT"
```

### Verifying changes

**Text-only changes** (prose edits, content rewrites): Run pre-commit and provide the dev server link. Playwright verification is not required.

**Visual changes** (CSS, layout, templates, responsive breakpoints): Use Playwright MCP to verify before returning. Visual bugs often hide in CSS specificity, template inheritance, and responsive behavior.

Playwright workflow for visual changes:
1. Navigate to affected page(s)
2. Take a snapshot to verify rendered output
3. Iterate if the result doesn't match expectations

Common visual issues to check:
- Text positioning and visibility
- Spacing and alignment
- Regressions on nearby elements
- Responsive behavior (use `browser_resize`)

**Always include the dev server link** when returning after doc changes:

```
View changes: http://127.0.0.1:<port>
```

## Theme architecture

The docs use a standalone "warm workbench" theme. Key files:

| File | Purpose |
|------|---------|
| `templates/_variables.html` | CSS custom properties (colors, layout, typography) |
| `sass/custom.scss` | All styling, organized by section |
| `templates/base.html` | Head overrides, iOS viewport polyfill |
| `templates/index.html` | Homepage hero and animations |
| `templates/page.html` | Doc page TOC rendering |

### Layout system

The sticky header and TOC use **definitional CSS variables** so positions are always in sync:

```
--wt-header-height: 60px    (includes border via box-sizing: border-box)
--wt-main-padding-top: 40px

TOC sticks at: calc(header + padding) = 100px
Anchor scroll-margin: same calculation
```

When either variable changes (including via media queries), all dependent values update automatically. This prevents the TOC from "jumping" when transitioning to sticky mode.

### Key technical decisions

1. **`box-sizing: border-box` on header** - Border is included in height, simplifying calculations
2. **`scrollbar-gutter: stable`** - Reserves scrollbar space to prevent layout shift on navigation
3. **IntersectionObserver intercept** - Ensures scroll-spy doesn't conflict with TOC styling
4. **Logo preload** - Prevents flash when navigating between pages
5. **WCAG AA colors** - `--wt-color-text-soft` is #78716a for 4.5:1 contrast
6. **iOS viewport polyfill** - Sets stable `--vh-full` variable for Firefox iOS/Chrome iOS (see `templates/base.html` for details on the jank issue and what we tried)

### Responsive breakpoints

Variables are overridden in media queries to maintain definitional correctness:

- **≤1024px**: `--wt-main-padding-top: 30px`
- **≤768px**: `--wt-header-height: 50px`, `--wt-main-padding-top: 20px`, TOC hidden

### Extending the theme

When adding new positioned elements:
- Use the layout variables rather than hardcoding pixel values
- Test anchor navigation to verify no visual jumps
- Check both with and without page scroll

## Command documentation

Command pages (e.g., `switch.md`, `merge.md`, `list.md`) are **generated from the CLI source code**. Each file has a **skeleton** (frontmatter) with a marker region that gets replaced by generated content.

### Generation mechanism

Each command page has this structure:

```markdown
+++
title = "wt list"
weight = 11

[extra]
group = "Commands"
+++

<!-- ⚠️ AUTO-GENERATED from `wt list --help-page` — edit cli.rs to update -->

[generated content here]

<!-- END AUTO-GENERATED -->
```

The bare close marker is paired with the open via non-greedy regex matching. `test_no_nested_auto_generated_markers` enforces that no `AUTO-GENERATED` open ever appears inside a region in `docs/content/*.md`, which is the precondition for non-greedy pairing to be safe.

The frontmatter (title, weight, group) is preserved in each file. Everything between the START and END markers is generated by:

```bash
wt <command> --help-page
```

The generated content includes:
1. **Conceptual documentation** — from `after_long_help` in `src/cli.rs`
2. **Command Reference section** — the standard `--help` output

### Editing command docs

**To update command documentation, edit `src/cli.rs`**, not the markdown files directly.

- **Conceptual content** — Edit the `after_long_help` attribute on the command
- **Usage/options/examples** — Edit the clap attributes (`about`, `long_about`, doc comments on args)

After editing, run the sync test (which auto-updates out-of-sync pages):

```bash
cargo test --test integration test_docs_are_in_sync
```

### Command documentation structure

Each command has three documentation pieces in `src/cli.rs`:

| Piece | Source | Purpose |
|-------|--------|---------|
| **Definition** | First `///` line | Short identifier for command lists |
| **Subdefinition** | Second `///` line (optional) | Adds context when relevant |
| **after_long_help** | `#[command(after_long_help = "...")]` | Full documentation |

**Where each appears:**

| Context | What's shown |
|---------|--------------|
| Command list (`wt --help`) | Definition only |
| Terminal `-h` | Definition in header |
| Terminal `--help` | Definition (header) + Subdefinition (under header) + after_long_help (after options) |
| Web docs | "Definition. Subdefinition." as lead paragraph, then after_long_help |

**Content principles:**

1. **Definition must be short** — It appears in command lists; keep it to a noun phrase or brief imperative.

2. **Subdefinition adds context** — Only include if it provides information the definition doesn't. If the definition is complete, omit the subdefinition.

3. **after_long_help must not repeat** — Start with NEW information that expands on or provides context for the definition. Each piece should add information the previous pieces didn't provide.

4. **after_long_help should mostly stand alone** — The opener should give context or purpose, not just continue with details that only make sense after reading the definition. Avoid non-sequiturs.

**Good patterns for after_long_help openers:**

- Explain the mental model: "Worktrees are addressed by branch name..."
- Contrast with similar tools: "Unlike `git merge`, this merges current into target..."
- State the use case: "Use when you're done with a feature branch."
- Explain what something is: "Shell commands that run at key points..."
- Describe relationship to other commands: "The building blocks of `wt merge`: commit, squash, rebase, push."

**Patterns to avoid:**

- Repeating the definition with different words
- Starting with details that assume context ("The table shows..." when there's no prior mention of a table)
- Leading with configuration defaults before establishing what the command does
- Non-sequiturs that jump to side-effects without context

### Example output expansion (wt list)

The `wt list` examples use **HTML comments + code blocks** that expand to full snapshot output. In `cli.rs`, you write:

`````
<!-- wt list -->
```console
wt list
```
`````

This renders differently in each context:
- **Terminal help (`--help`)**: HTML comment skipped, code block shows as dimmed `wt list`
- **Web docs (`--help-page`)**: Both are replaced with the standard template format:

```
<!-- ⚠️ AUTO-GENERATED from tests/snapshots/<snapshot_file> — edit source to update -->

{% terminal() %}
<span class="prompt">$</span> <span class="cmd">wt list</span>
<output...>
{% end %}

<!-- END AUTO-GENERATED -->
```

**The mapping** (in `tests/integration_tests/readme_sync.rs`):

| Placeholder | Snapshot File |
|-------------|---------------|
| `<!-- wt list -->` | `readme_example_list.snap` |
| `<!-- wt list --full -->` | `readme_example_list_full.snap` |
| `<!-- wt list --branches --full -->` | `readme_example_list_branches.snap` |

**To update example output:**

1. Edit test setup in `tests/integration_tests/list.rs` → `setup_readme_example_repo()`
2. Run tests to regenerate snapshots: `cargo test --test integration readme_example_list`
3. Accept snapshots: `cargo insta accept`
4. Sync docs: `cargo test --test integration test_docs_are_in_sync`

The examples in `cli.rs` are just command stubs — the actual output comes from snapshots generated by integration tests. This ensures docs always match real CLI behavior.

### Code block convention

All shell commands use `$ ` prefix in `` ```console `` blocks. `convert_dollar_console_to_terminal()` (`src/docs.rs`, shared library function) converts them to terminal shortcodes. The sync test also runs this on hand-written docs.

| Detected pattern | Web output | Highlighting |
|------------------|------------|--------------|
| `$ ` commands | `cmd` parameter with `\|\|\|` delimiter | Full Syntect |
| No `$ ` | `console` → `bash` conversion | Syntect (no `$ ` prompt) |

All `$ ` commands go through the `cmd` parameter path for consistent Syntect highlighting. Multiple commands are joined by `|||`; the template splits and highlights each individually. Commands are wrapped in `<span class="cmd">` (CSS `::before` adds `$ `). Comment lines (`#`) are highlighted but not wrapped (no prompt).

**Placeholders in cmd values:** Tera (Zola's template engine) would interpret `{{ }}` in the `cmd` parameter as template expressions, and `"` would close the parameter string. Since Tera has no backslash-escape mechanism for string literals, these characters are replaced with text placeholders (`__WT_OPEN__`, `__WT_CLOSE__`, `__WT_QUOT__`) in the Rust conversion function. The terminal shortcode template replaces them back before Syntect highlighting. The sync test also strips them when generating skill reference files.

Hand-written docs can use either `console` fences with `$ ` (auto-converted by the sync test) or shortcodes directly.

### CLI and web compatibility

Content in `after_long_help` must work in **both** the terminal (`--help`) and the web docs:

- **Tables** — Work in both. Prefer tables over bullet lists for structured data.
- **Markdown links** — Work in both (`[text](/path/)`)
- **Code blocks** — Work in both
- **Raw HTML** — Avoid. Renders as raw text in terminal help.

### Post-processing for web docs

The `--help-page` generator in `src/main.rs` applies post-processing to transform CLI-friendly content into web-friendly HTML:

| CLI Source | Web Output |
|------------|------------|
| `` ```console `` with `$ ` | `terminal` shortcode with `cmd` parameter |
| `` ```console `` (no `$ `) | `` ```bash `` |
| `` `●` green `` | `<span style='color:#0a0'>●</span> green` |
| `` `●` blue `` | `<span style='color:#00a'>●</span> blue` |
| `` `●` red `` | `<span style='color:#a00'>●</span> red` |
| `` `●` yellow `` | `<span style='color:#a60'>●</span> yellow` |
| `` `●` gray `` | `<span style='color:#888'>●</span> gray` |
| `[experimental]` | `<span class="badge-experimental"></span>` (text via CSS `::after`) |

To add web-only styling for new content, edit `post_process_for_html()` in `src/help.rs` — not the markdown files.

Similarly, `md_help::colorize_status_symbols()` applies ANSI colors for terminal `--help` output.

### Subdoc expansion

Include subcommand documentation as H2 sections within a parent command's docs page using subdoc placeholders:

```markdown
<!-- subdoc: subcommand-name -->
```

In `cli.rs`, add the placeholder anywhere in the parent command's `after_long_help`:

```rust
after_long_help = r#"...main documentation...

<!-- subdoc: create -->

...more documentation..."#
```

This expands during `--help-page` generation to:

```markdown
## wt config create

### User config
[subcommand's after_long_help content with heading levels increased]

---

### Command reference
[subcommand's usage and options]
```

**How it works:**
- The placeholder is invisible in terminal `--help` output (HTML comment)
- Heading levels in the subcommand's `after_long_help` are increased by one (## → ###)
- The subcommand's help reference is formatted as a nested `### Command reference`

**Use cases:**
- `wt config create` — Shows the actual config file templates (via `include_str!`)
- `wt config state marker` — Shows per-key examples not in the parent

All AUTO-GENERATED markers use a consistent format with START and END tags:
```html
<!-- ⚠️ AUTO-GENERATED from <source> — edit <file> to update -->
[content]
<!-- END AUTO-GENERATED -->
```

All `AUTO-GENERATED` regions use the bare close — sync test
`test_no_nested_auto_generated_markers` enforces that no region ever nests
inside another in `docs/content/*.md`, which is the precondition for the
outer-region regex to safely pair the open with the close via non-greedy
matching. If you ever need nested regions, restore a disambiguating close
form on the outer marker rather than relying on the regex.

## Template examples in documentation

**All template examples must have corresponding tests** in `tests/integration_tests/doc_templates.rs`. This catches issues like operator precedence bugs (PR #373).

When adding template examples, add a test that verifies the template produces expected output. See existing tests for patterns.

## Demo GIF workflow

Demo GIFs (~2MB each) are stored in a separate `worktrunk-assets` repo to avoid bloating git history. Both build and fetch output to `docs/static/assets/` (gitignored), so local builds override fetched assets.

**For local development:**
```bash
task fetch-assets       # Download published assets
```

**To regenerate demos** (required after CLI output changes):

```bash
./docs/demos/build docs      # Doc site demos (light + dark)
./docs/demos/build social    # Social media demos (light only)
task publish-assets          # Publish to assets repo
```

Deploy runs `fetch-assets` before building.

For detailed demo development guidelines (timing, debugging, environment setup), see `docs/demos/CLAUDE.md`.

## Social card workflow

Social cards follow the same assets pattern as demos.

**Source files:**
- `social-card.svg` (1200×630) — Open Graph/Twitter link previews, referenced in `base.html`
- `github-social-card.svg` (1280×640) — GitHub repository preview, uploaded manually in repo Settings → Social preview

**To regenerate** (after changing tagline, logo, or layout):

```bash
task build-social-cards    # SVG → PNG (downloads fonts if needed)
task publish-assets        # Publish to assets repo
```

The build script automatically downloads Inter and Plus Jakarta Sans fonts from GitHub if not installed locally. Requires `rsvg-convert` (from librsvg).

**Referenced in:** `docs/templates/base.html` (og:image, twitter:image meta tags)

### Light/dark theme variants

In markdown, use `<picture>` with media queries:
```html
<figure class="demo">
<picture>
  <source srcset="/assets/docs/dark/wt-switch-picker.gif" media="(prefers-color-scheme: dark)">
  <img src="/assets/docs/light/wt-switch-picker.gif" alt="wt switch picker demo" width="1600" height="800">
</picture>
</figure>
```

The browser automatically shows the appropriate variant based on system preference.