worktrunk 0.38.0

A CLI for Git worktree management, designed for parallel AI agent workflows
Documentation
{% import "macros.html" as macros %}
<!DOCTYPE html>
<html lang="en">

<head>
    <script>
    // 1Password extension bug workaround: MUST be first script in document.
    // 1Password injects Prism.js targeting [class*="language-"], stripping our
    // syntect spans. Fix: rename language-* to lang-* before 1Password sees them.
    // https://1password.community/discussion/165597
    (function() {
        function fix(el) {
            if (el.className && el.className.replace) {
                el.className = el.className.replace(/\blanguage-\S*/g, '');
            }
            if (el.dataset && el.dataset.lang) el.removeAttribute('data-lang');
        }
        new MutationObserver(function(mutations) {
            for (var i = 0; i < mutations.length; i++) {
                var nodes = mutations[i].addedNodes;
                for (var j = 0; j < nodes.length; j++) {
                    var n = nodes[j];
                    if (n.nodeType === 1) {
                        if (n.className && n.className.includes && n.className.includes('language-')) fix(n);
                        var els = n.querySelectorAll ? n.querySelectorAll('[class*="language-"]') : [];
                        for (var k = 0; k < els.length; k++) fix(els[k]);
                    }
                }
            }
        }).observe(document.documentElement, { childList: true, subtree: true });
    })();
    </script>
    <meta charset="UTF-8">
    <title>{% block title %}{{ config.title }}{% endblock title %}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <link rel="icon" type="image/png" sizes="48x48" href="/favicon-48.png">
    <link rel="icon" type="image/png" sizes="32x32" href="/favicon.png">
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">

    {% include "_variables.html" %}

    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Plus+Jakarta+Sans:wght@500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
    <link rel="preload" href="{{ get_url(path='logo@2x.png') }}" as="image" media="(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)">
    <link rel="preload" href="{{ get_url(path='logo.png') }}" as="image" media="not all and (-webkit-min-device-pixel-ratio: 2), not all and (min-resolution: 192dpi)">

    <script>
    // iOS viewport jank fix: Firefox/Chrome iOS have broken svh/lvh units that
    // behave like dvh, causing the hero to resize when browser chrome appears.
    // Root cause: Missing WebKit minimumViewportInset/maximumViewportInset APIs.
    // https://github.com/mozilla-mobile/firefox-ios/issues/11574
    //
    // Solution: Capture height ONCE on load, set --vh-full CSS variable.
    // CSS uses var(--vh-full, 100lvh) so iOS gets stable pixels, others get lvh.
    (function() {
        var isIOS = /iP(hone|ad|od)/.test(navigator.platform) ||
                    (/Macintosh/.test(navigator.userAgent) && 'ontouchend' in document);
        if (!isIOS) return;

        var h = window.innerHeight;
        if (h) {
            document.documentElement.style.setProperty('--vh-full', h + 'px');
        }

        window.addEventListener('orientationchange', function() {
            setTimeout(function() {
                var newH = window.innerHeight;
                if (newH) {
                    document.documentElement.style.setProperty('--vh-full', newH + 'px');
                }
            }, 100);
        });
    })();
    </script>
    <meta name="view-transition" content="same-origin">
    {% set page_description = page.description | default(value=config.extra.site_description) %}
    <meta name="description" content="{{ page_description }}">
    {% if page.extra.canonical %}
    <link rel="canonical" href="{{ config.base_url | trim_end_matches(pat='/') }}{{ page.extra.canonical }}">
    {% else %}
    <link rel="canonical" href="{{ current_url | default(value=config.base_url) }}">
    {% endif %}

    <!-- OpenGraph -->
    {% set og_title = page.title | default(value="") %}
    <meta property="og:type" content="website">
    <meta property="og:url" content="{% if page.extra.canonical %}{{ config.base_url | trim_end_matches(pat='/') }}{{ page.extra.canonical }}{% else %}{{ current_url | default(value=config.base_url) }}{% endif %}">
    {% if og_title %}{% set og_title_full = og_title ~ " | " ~ config.title %}{% else %}{% set og_title_full = config.title ~ " — " ~ config.extra.site_description %}{% endif %}
    <meta property="og:title" content="{{ og_title_full }}">
    <meta property="og:description" content="{{ page_description }}">
    <meta property="og:image" content="{{ config.base_url }}assets/social/social-card.png">
    <meta property="og:image:width" content="1200">
    <meta property="og:image:height" content="630">

    <!-- Twitter Card -->
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:title" content="{{ og_title_full }}">
    <meta name="twitter:description" content="{{ page_description }}">
    <meta name="twitter:image" content="{{ config.base_url }}assets/social/social-card.png">

    <!-- Structured data -->
    {% if not page.title %}
    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "WebSite",
      "name": "Worktrunk",
      "url": "https://worktrunk.dev/",
      "description": "{{ config.extra.site_description }}"
    }
    </script>
    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "SoftwareApplication",
      "name": "Worktrunk",
      "url": "https://worktrunk.dev/",
      "description": "{{ config.extra.site_description }}",
      "applicationCategory": "DeveloperApplication",
      "operatingSystem": "macOS, Linux, Windows",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "USD"
      },
      "codeRepository": "https://github.com/max-sixty/worktrunk",
      "license": "https://opensource.org/licenses/Apache-2.0"
    }
    </script>
    {% endif %}

    <!-- Google tag (gtag.js) -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-6YY8BDJ4QH"></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', 'G-6YY8BDJ4QH');
    </script>

    <link rel="stylesheet" href="/normalize.css">
    <link rel="preload" href="/custom.css" as="style">
    <link rel="stylesheet" href="/custom.css">
    <!-- Syntax highlighting themes - preload and load conditionally by color scheme -->
    <link rel="preload" href="/giallo-light.css" as="style" media="(prefers-color-scheme: light)">
    <link rel="preload" href="/giallo-dark.css" as="style" media="(prefers-color-scheme: dark)">
    <link rel="stylesheet" href="/giallo-light.css" media="(prefers-color-scheme: light)">
    <link rel="stylesheet" href="/giallo-dark.css" media="(prefers-color-scheme: dark)">
    <script src="/elasticlunr.min.js" defer></script>
    <script src="/search_index.en.js" defer></script>
    <script src="/search.js" defer></script>
    <script src="/code-copy.js" defer></script>
    <script src="/mobile-menu.js" defer></script>
    <script src="//instant.page/5.2.0" type="module" integrity="sha384-jnZyxPjiipYXnSU0ygqeac2q7CVYMbh84q0uHVRRxEtvFPiQYbXWUorga2aqZJ0z"></script>
    <script>
    // Show demo images with fade-in after they load (hides broken-image icon during loading)
    document.addEventListener('DOMContentLoaded', function() {
        document.querySelectorAll('figure.demo img').forEach(function(img) {
            if (img.complete) {
                img.classList.add('loaded');
            } else {
                img.addEventListener('load', function() { img.classList.add('loaded'); });
            }
        });
    });
    </script>
</head>

<body>
    <!-- iOS 26 Liquid Glass fix: solid scrim behind translucent browser chrome -->
    <div class="ios-chrome-scrim" aria-hidden="true"></div>
    <header>
        <button class="mobile-menu-toggle" aria-label="Open menu" aria-expanded="false">
            <svg class="menu-icon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <line x1="3" y1="6" x2="21" y2="6"></line>
                <line x1="3" y1="12" x2="21" y2="12"></line>
                <line x1="3" y1="18" x2="21" y2="18"></line>
            </svg>
            <svg class="close-icon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <line x1="18" y1="6" x2="6" y2="18"></line>
                <line x1="6" y1="6" x2="18" y2="18"></line>
            </svg>
        </button>
        <a href="/">
            <div class="logo">
                <img src="{{ get_url(path=config.extra.logo_path) }}" srcset="{{ get_url(path='logo.png') }} 1x, {{ get_url(path='logo@2x.png') }} 2x" alt="logo" width="44" height="44" decoding="sync" fetchpriority="high">
                {{ config.extra.logo_name }}
            </div>
        </a>
        <nav>
            <div class="search-container">
                <input id="userinput" type="search" placeholder="Search..." aria-label="Search documentation">
                <div id="suggestions"></div>
            </div>
            {% for menu in config.extra.extra_menu %}
            <a class="nav-item" href="{{ menu.link | safe }}">{{ menu.title }}</a>
            {% endfor %}
        </nav>
    </header>

    <!-- Mobile menu drawer -->
    <div class="mobile-menu-overlay" aria-hidden="true"></div>
    <nav class="mobile-menu" aria-hidden="true">
        <div class="mobile-menu-content">
            <div class="mobile-menu-section">
                {% for menu in config.extra.extra_menu %}
                <a class="mobile-nav-item" href="{{ menu.link | safe }}">{{ menu.title }}</a>
                {% endfor %}
            </div>
            <div class="mobile-menu-divider"></div>
            <div class="mobile-menu-section mobile-menu-toc">
                {% set docs_section = get_section(path="_index.md") %}
                {% set current_group = "" %}
                {% for doc_page in docs_section.pages %}
                {% set page_group = doc_page.extra.group | default(value="") %}
                {% if page_group != current_group and page_group != "" %}
                <div class="mobile-toc-group">{{ page_group }}</div>
                {% set_global current_group = page_group %}
                {% endif %}
                <a class="mobile-toc-item" href="{{ doc_page.permalink | safe }}">{{ doc_page.title }}</a>
                {% endfor %}
            </div>
        </div>
    </nav>

    <div class="hero">
        {% block hero %}{% endblock hero %}
    </div>

    <main>
        {% block toc %}
        {% set docs_section = get_section(path="_index.md") %}
        {{ macros::toc_nav(docs_section=docs_section) }}
        {% endblock toc %}

        <div class="content" id="{% block content_id %}{% endblock content_id %}">
            {% block content %}
            {{ section.content | safe }}
            {% endblock content %}
        </div>
    </main>

    <footer>
        <div class="footer-social">
            <a href="https://github.com/max-sixty/worktrunk" aria-label="GitHub" title="GitHub">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/>
                </svg>
            </a>
            <a href="https://crates.io/crates/worktrunk" aria-label="Crates.io" title="Crates.io">
                <svg width="20" height="20" viewBox="0 0 512 512" fill="currentColor">
                    <path d="M239.1 6.3l-208 78c-18.7 7-31.1 25-31.1 45v225.1c0 18.2 10.3 34.8 26.5 42.9l208 104c13.5 6.8 29.4 6.8 42.9 0l208-104c16.3-8.1 26.5-24.8 26.5-42.9V129.3c0-20-12.4-37.9-31.1-44.9l-208-78C262.4 2.2 249.6 2.2 239.1 6.3zM256 68.4l192 72v1.1l-192 78-192-78v-1.1l192-72zm32 356V275.5l160-65v160.4l-160 53.5z"/>
                </svg>
            </a>
            <a href="https://twitter.com/intent/tweet?text=Worktrunk%20%E2%80%94%20CLI%20for%20git%20worktree%20management&url=https%3A%2F%2Fworktrunk.dev" aria-label="Share on X" title="Share on X">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
                </svg>
            </a>
            <a href="https://www.reddit.com/submit?url=https%3A%2F%2Fworktrunk.dev&title=Worktrunk%20%E2%80%94%20CLI%20for%20git%20worktree%20management" aria-label="Share on Reddit" title="Share on Reddit">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z"/>
                </svg>
            </a>
            <a href="https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fworktrunk.dev" aria-label="Share on LinkedIn" title="Share on LinkedIn">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
                </svg>
            </a>
        </div>
    </footer>
    <script>
    // Wrap code blocks in containers SYNCHRONOUSLY before first paint to prevent layout jump.
    // The deferred code-copy.js will add copy buttons to these wrappers later.
    (function() {
        var blocks = document.querySelectorAll('.content pre');
        for (var i = 0; i < blocks.length; i++) {
            var block = blocks[i];
            var wrapper = document.createElement('div');
            wrapper.className = 'code-block-wrapper';
            block.parentNode.insertBefore(wrapper, block);
            wrapper.appendChild(block);
        }
    })();
    </script>
</body>

</html>