1241 lines
76 KiB
HTML
1241 lines
76 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Herdr: one terminal for the whole herd</title>
|
||
<meta
|
||
name="description"
|
||
content="Herdr is to coding agents what tmux is to terminals: an agent multiplexer that runs where your agents run. Real panes, agent state at a glance, ssh from anywhere, no app."
|
||
/>
|
||
<meta name="theme-color" content="#f0eee9" />
|
||
<link rel="canonical" href="https://herdr.dev/" />
|
||
|
||
<meta property="og:type" content="website" />
|
||
<meta property="og:site_name" content="Herdr" />
|
||
<meta property="og:title" content="Herdr: one terminal for the whole herd" />
|
||
<meta
|
||
property="og:description"
|
||
content="To coding agents what tmux is to terminals. Runs where your agents run: close the laptop, ssh from anywhere, nothing dies."
|
||
/>
|
||
<meta property="og:url" content="https://herdr.dev/" />
|
||
<meta
|
||
property="og:image"
|
||
content="https://herdr.dev/assets/og-card-v8.png"
|
||
/>
|
||
<meta property="og:image:width" content="1200" />
|
||
<meta property="og:image:height" content="630" />
|
||
<meta property="og:image:alt" content="Herdr. One terminal. The whole herd." />
|
||
|
||
<meta name="twitter:card" content="summary_large_image" />
|
||
<meta name="twitter:title" content="Herdr: one terminal for the whole herd" />
|
||
<meta
|
||
name="twitter:description"
|
||
content="To coding agents what tmux is to terminals. Runs where your agents run: close the laptop, ssh from anywhere, nothing dies."
|
||
/>
|
||
<meta
|
||
name="twitter:image"
|
||
content="https://herdr.dev/assets/og-card-v8.png"
|
||
/>
|
||
<meta name="twitter:image:alt" content="Herdr. One terminal. The whole herd." />
|
||
|
||
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon.png?v=14" />
|
||
<link rel="apple-touch-icon" href="/assets/logo.png" />
|
||
<link
|
||
rel="preload"
|
||
href="/assets/jbm.woff2"
|
||
as="font"
|
||
type="font/woff2"
|
||
crossorigin
|
||
/>
|
||
<script>
|
||
(function () {
|
||
var saved = localStorage.getItem("herdr-palette");
|
||
var system = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||
? "tokyo-night"
|
||
: "tokyo-night-day";
|
||
document.documentElement.setAttribute("data-palette", saved || system);
|
||
})();
|
||
</script>
|
||
<link rel="stylesheet" href="/css/style.css?v=42" />
|
||
</head>
|
||
<body>
|
||
<header class="site-header">
|
||
<nav class="nav" aria-label="Primary navigation">
|
||
<a class="brand" href="/" aria-label="Herdr home">
|
||
<span class="brand-logo" aria-hidden="true"></span>
|
||
<span>herdr</span>
|
||
</a>
|
||
<details class="nav-menu">
|
||
<summary class="nav-menu-button" aria-label="Open navigation">
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
</summary>
|
||
<div class="nav-menu-panel">
|
||
<a href="/">Home</a>
|
||
<a href="/compare/">Compare</a>
|
||
<a href="/plugins/">Plugins</a>
|
||
<a href="/stats/">Stats</a>
|
||
<a href="/blog/">Blog</a>
|
||
<a href="/docs/quick-start/">Quick start</a>
|
||
<a href="/docs/">Docs</a>
|
||
<a href="/docs/socket-api/">API</a>
|
||
<a href="/docs/install/">Install</a>
|
||
<a href="https://github.com/ogulcancelik/herdr">GitHub</a>
|
||
</div>
|
||
</details>
|
||
<div class="nav-links">
|
||
<a href="/">Home</a>
|
||
<a href="/compare/">Compare</a>
|
||
<a href="/plugins/">Plugins</a>
|
||
<a href="/stats/">Stats</a>
|
||
<a href="/blog/">Blog</a>
|
||
<a href="/docs/quick-start/">Quick start</a>
|
||
<a href="/docs/">Docs</a>
|
||
<a href="/docs/socket-api/">API</a>
|
||
</div>
|
||
<div class="nav-tools">
|
||
<a class="nav-icon-link" href="https://github.com/ogulcancelik/herdr" aria-label="Herdr on GitHub">
|
||
<svg viewBox="0 0 16 16" aria-hidden="true">
|
||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82A7.64 7.64 0 0 1 8 3.87c.68 0 1.36.09 2 .26 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" />
|
||
</svg>
|
||
</a>
|
||
<a class="nav-icon-link" href="https://x.com/herdrdev" aria-label="Herdr on X">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||
<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 class="nav-cta" href="/docs/install/">Install</a>
|
||
</div>
|
||
</nav>
|
||
</header>
|
||
|
||
<main>
|
||
<section class="hero" id="hero">
|
||
<div class="hero-left">
|
||
<div class="hero-copy">
|
||
<p class="eyebrow">
|
||
<span class="status-dot dot-done"></span>
|
||
Agent multiplexer · a binary, not an app
|
||
</p>
|
||
<h1>One terminal. The whole herd.</h1>
|
||
<p class="hero-lede">
|
||
Run all your coding agents from one terminal, on any
|
||
box, even over ssh. Each runs in its own real terminal,
|
||
on a server that keeps it alive when you close the
|
||
laptop. See
|
||
<span class="state blocked">blocked</span>,
|
||
<span class="state working">working</span>, and
|
||
<span class="state done">done</span> at a glance, and
|
||
reattach from your phone.
|
||
</p>
|
||
<div class="hero-actions">
|
||
<a class="button button-primary" href="/docs/quick-start/"
|
||
>Quick start</a
|
||
>
|
||
<a class="button button-secondary" href="/docs/"
|
||
>Read the docs</a
|
||
>
|
||
</div>
|
||
<div class="install-panel" aria-label="Install options">
|
||
<div class="install-methods" role="group" aria-label="Install method">
|
||
<span class="install-label">Stable install</span>
|
||
<button
|
||
class="install-method is-active"
|
||
type="button"
|
||
data-command="curl -fsSL https://herdr.dev/install.sh | sh"
|
||
data-size="compact"
|
||
aria-pressed="true"
|
||
>Script</button>
|
||
<button
|
||
class="install-method"
|
||
type="button"
|
||
data-command="brew install herdr"
|
||
data-size="normal"
|
||
aria-pressed="false"
|
||
>Homebrew</button>
|
||
<button
|
||
class="install-method"
|
||
type="button"
|
||
data-command="nix run github:ogulcancelik/herdr"
|
||
data-size="normal"
|
||
aria-pressed="false"
|
||
>Nix flake</button>
|
||
</div>
|
||
<div class="install-card" aria-label="Install command">
|
||
<span class="prompt">$</span>
|
||
<code id="install-cmd" class="is-compact"
|
||
>curl -fsSL https://herdr.dev/install.sh | sh</code
|
||
>
|
||
<button id="copy-btn" type="button" data-copy-target="install-cmd">Copy</button>
|
||
</div>
|
||
<div class="windows-install" aria-label="Windows beta preview install">
|
||
<div class="windows-install-heading">
|
||
<span>Windows beta · preview only</span>
|
||
<a href="/docs/preview/windows-beta/">Preview docs</a>
|
||
</div>
|
||
<div class="install-card install-card-secondary" aria-label="Windows beta install command">
|
||
<span class="prompt">PS></span>
|
||
<code id="windows-install-cmd" class="is-compact"
|
||
>irm https://herdr.dev/install.ps1 | iex</code
|
||
>
|
||
<button type="button" data-copy-target="windows-install-cmd">Copy</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="hero-note">Stable Linux/macOS · Windows preview beta · no Electron, no account, no telemetry</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="hero-right">
|
||
<figure class="hero-product-shot">
|
||
<div
|
||
class="herdr-ui-mock"
|
||
id="herd-mock"
|
||
role="img"
|
||
aria-label="Interactive Herdr session mock with workspaces, tabs, agent states, and terminal panes running Claude Code and Codex"
|
||
>
|
||
<aside class="mock-sidebar">
|
||
<div class="mock-sidebar-title">spaces</div>
|
||
<div class="mock-space active" data-ws="herdr">
|
||
<span class="mock-dot dot-working"></span>
|
||
<div><strong>herdr</strong><small>master</small></div>
|
||
</div>
|
||
<div class="mock-space" data-ws="llm-proxy">
|
||
<span class="mock-dot dot-blocked"></span>
|
||
<div><strong>llm-proxy</strong><small>master</small></div>
|
||
</div>
|
||
<div class="mock-space" data-ws="qmp">
|
||
<span class="mock-dot dot-done"></span>
|
||
<div><strong>qmp</strong><small>autoresearch/qmp-ra…</small></div>
|
||
</div>
|
||
<div class="mock-sidebar-spacer"></div>
|
||
<div class="mock-agent-title"><span>agents</span><span>all</span></div>
|
||
<div class="mock-agent working" data-agent="herdr" data-kind="claude" data-ws-link="herdr" data-tab-link="main"><span class="braille-spinner"></span><div><strong>herdr</strong><small>working · claude</small></div></div>
|
||
<div class="mock-agent" data-agent="explore" data-kind="opencode" data-ws-link="herdr" data-tab-link="opencode"><span>✓</span><div><strong>explore</strong><small>idle · opencode</small></div></div>
|
||
<div class="mock-agent blocked" data-agent="llm-proxy" data-kind="claude" data-ws-link="llm-proxy" data-tab-link="main"><span>◉</span><div><strong>llm-proxy</strong><small>blocked · claude</small></div></div>
|
||
<div class="mock-agent done" data-agent="qmp" data-kind="codex" data-ws-link="qmp" data-tab-link="main"><span>●</span><div><strong>qmp</strong><small>done · codex</small></div></div>
|
||
</aside>
|
||
<section class="mock-main">
|
||
<div class="mock-view active" data-view="herdr">
|
||
<div class="mock-tabs">
|
||
<span data-tab="opencode">opencode</span><span class="active" data-tab="main">herdr</span><span>+</span>
|
||
</div>
|
||
<div class="mock-panes active" data-tab="main">
|
||
<div class="mock-pane focused tui">
|
||
<div class="cc-head"><span class="cc-mark">✻</span> <b>Claude Code</b> <span class="dim">v2.1.168</span></div>
|
||
<div class="dim"> ~/Projects/herdr · master</div>
|
||
<br />
|
||
<div><b>❯</b> the sidebar flickers when an agent flips state fast</div>
|
||
<br />
|
||
<div><span class="cc-bullet">●</span> Found it. The rollup recomputes on every PTY chunk, so a double state flip repaints the dot mid-frame.</div>
|
||
<br />
|
||
<div><span class="cc-bullet">●</span> Plan:</div>
|
||
<div class="dim"> · src/app/state.rs — buffer state events per tick</div>
|
||
<div class="dim"> · src/ui/sidebar.rs — repaint rollups once per frame</div>
|
||
<br />
|
||
<div><b>❯</b> go, and add a regression test</div>
|
||
<br />
|
||
<div><span class="cc-working"><span class="braille-spinner">⠋</span> Working…</span> <span class="dim">(<span id="cc-timer">13m 36s</span> · esc to interrupt)</span></div>
|
||
<div class="tui-bottom">
|
||
<div class="cc-rule"></div>
|
||
<div><b>❯</b> <span class="cursor"></span></div>
|
||
<div class="cc-rule"></div>
|
||
<div class="dim">~/Projects/herdr > master > ctx ─── 3% 34k/1M</div>
|
||
<div class="dim">>> bypass permissions on (shift+tab to cycle)</div>
|
||
</div>
|
||
</div>
|
||
<div class="mock-pane">
|
||
<div class="mock-prompt"><span>~/Projects/herdr</span> <em>master</em></div>
|
||
<div><b>❯</b> <span class="cmd">bun</span> run dev</div>
|
||
<div class="dim">$ node scripts/prepare-docs.mjs && astro dev</div>
|
||
<div><span class="time">02:10:44</span> <span class="blue">[types]</span> Generated 0ms</div>
|
||
<div><span class="time">02:10:44</span> <span class="blue">[content]</span> Synced content</div>
|
||
<br />
|
||
<div><span class="badge">astro</span> <span class="green">v5.18.1</span> ready in 668 ms</div>
|
||
<br />
|
||
<div class="url-line">┃ Local <span>http://localhost:4321/</span></div>
|
||
<div class="dim"> Network use --host to expose</div>
|
||
<br />
|
||
<div><span class="time">02:10:44</span> <span class="green">watching for file changes...</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="mock-panes single" data-tab="opencode">
|
||
<div class="mock-pane focused tui">
|
||
<div class="oc-y">+ Thought: 468ms</div>
|
||
<br />
|
||
<div><span class="dim">→</span> Read src/main.rs</div>
|
||
<br />
|
||
<div class="oc-y">+ Thought: 292ms</div>
|
||
<br />
|
||
<div>This is <span class="oc-y">src/main.rs</span> — the entry point for <b>herdr</b>. Here's what it does:</div>
|
||
<br />
|
||
<div><span class="blue">1.</span> Arg parsing — <span class="oc-y">session::configure_from_args</span>, then remote args for the SSH bridge.</div>
|
||
<div><span class="blue">2.</span> Subcommand dispatch — <span class="oc-y">server</span>, <span class="oc-y">client</span>, <span class="oc-y">update</span> — each returns early.</div>
|
||
<div><span class="blue">3.</span> Session mode — <span class="oc-y">auto_detect_launch()</span> connects to a running server or spawns one.</div>
|
||
<br />
|
||
<div class="dim"><span class="oc-badge"></span> Build · MiMo V2.5 Free · 16.6s</div>
|
||
<div class="tui-bottom">
|
||
<div class="oc-input">
|
||
<div><span class="cursor"></span></div>
|
||
<div class="dim"><span class="oc-purple">Build</span> · MiMo V2.5 Free OpenCode Zen</div>
|
||
</div>
|
||
<div class="oc-status"><span class="dim">25.2K (13%) ctrl+p commands</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="mock-view" data-view="llm-proxy">
|
||
<div class="mock-tabs">
|
||
<span class="active" data-tab="main">migrate</span><span data-tab="logs">logs</span><span>+</span>
|
||
</div>
|
||
<div class="mock-panes single active" data-tab="main">
|
||
<div class="mock-pane focused tui">
|
||
<div class="cc-head"><span class="cc-mark">✻</span> <b>Claude Code</b> <span class="dim">v2.1.168</span></div>
|
||
<div class="dim"> ~/Projects/llm-proxy · master</div>
|
||
<br />
|
||
<div><b>❯</b> prep the request-log migration and run it</div>
|
||
<br />
|
||
<div><span class="cc-bullet">●</span> Wrote migrations/0007_request_log.sql — request_log table, retention index on created_at, daily rollup view.</div>
|
||
<br />
|
||
<div><span class="cc-bullet">●</span> Local checks pass: sqlx prepare is clean, backfill dry-run touches 41,209 rows. Ready to apply.</div>
|
||
<div class="tui-bottom">
|
||
<div class="cc-permission">
|
||
<div><b>Bash command</b></div>
|
||
<div><span class="cmd">cargo</span> sqlx migrate run</div>
|
||
<div class="dim">Apply pending database migrations</div>
|
||
<br />
|
||
<div>Do you want to proceed?</div>
|
||
<div class="sel">❯ 1. Yes</div>
|
||
<div class="dim"> 2. No, and tell Claude what to do differently (esc)</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="mock-panes single" data-tab="logs">
|
||
<div class="mock-pane focused">
|
||
<div class="mock-prompt"><span>~/Projects/llm-proxy</span> <em>master</em></div>
|
||
<div><b>❯</b> <span class="cmd">tail</span> -f logs/proxy.log</div>
|
||
<div><span class="time">02:30:58</span> <span class="blue">INFO</span> request id=4818 route=/v1/messages 200 312ms</div>
|
||
<div><span class="time">02:31:04</span> <span class="blue">INFO</span> request id=4819 route=/v1/messages 200 287ms</div>
|
||
<div><span class="time">02:31:09</span> <span class="dim">DEBUG</span> cache hit ratio 0.93</div>
|
||
<div><span class="time">02:31:12</span> <span class="blue">INFO</span> request id=4820 route=/v1/messages 200 301ms</div>
|
||
<div><span class="time">02:31:15</span> <span class="red">WARN</span> upstream latency p99 1.8s</div>
|
||
<div><span class="time">02:31:18</span> <span class="blue">INFO</span> request id=4821 route=/v1/messages 200 295ms</div>
|
||
<div><span class="time">02:31:21</span> <span class="red">WARN</span> 429 from upstream, retrying in 2s</div>
|
||
<div><span class="time">02:31:24</span> <span class="blue">INFO</span> request id=4822 route=/v1/messages 200 1102ms</div>
|
||
<div><span class="time">02:31:30</span> <span class="dim">DEBUG</span> cache hit ratio 0.94</div>
|
||
<div><span class="time">02:31:33</span> <span class="blue">INFO</span> request id=4823 route=/v1/embeddings 200 88ms</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="mock-view" data-view="qmp">
|
||
<div class="mock-tabs">
|
||
<span class="active" data-tab="main">codex</span><span data-tab="server">server</span><span>+</span>
|
||
</div>
|
||
<div class="mock-panes single active" data-tab="main">
|
||
<div class="mock-pane focused tui">
|
||
<div><span class="cx-bullet">•</span> <b>Explored</b></div>
|
||
<div class="dim"> └ Read live_handoff.rs</div>
|
||
<br />
|
||
<div><span class="cx-bullet">•</span> <b>Ran</b></div>
|
||
<div class="dim"> └ cargo nextest run -E 'test(live_handoff)'</div>
|
||
<div class="dim"> └ 14 passed · 0 failed · 41.3s</div>
|
||
<br />
|
||
<div><span class="cx-bullet">•</span> The handoff suite passes. Pane PTYs survive server replacement, and long-lived workloads — including the http.server in the next tab — keep responding afterward.</div>
|
||
<br />
|
||
<div><span class="cx-bullet">•</span> Next I can wire the soak test into nightly CI and fail on p95 regressions. Want that?</div>
|
||
<div class="tui-bottom">
|
||
<div class="cc-rule"></div>
|
||
<div><b>›</b> <span class="cursor"></span></div>
|
||
<br />
|
||
<div class="dim">gpt-5.5 medium · ~/qmp · master · Context 6% used</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="mock-panes single" data-tab="server">
|
||
<div class="mock-pane focused">
|
||
<div class="mock-prompt"><span>~/qmp</span> <em>master</em></div>
|
||
<div><b>❯</b> <span class="cmd">python3</span> -m http.server 8080</div>
|
||
<div class="dim">Serving HTTP on 0.0.0.0 port 8080 ...</div>
|
||
<div><span class="time">02:28:41</span> "GET /runs/latest.json" <span class="green">200</span></div>
|
||
<div><span class="time">02:29:03</span> "GET /runs/latest.json" <span class="green">200</span></div>
|
||
<div><span class="time">02:29:25</span> "GET /qmp/report.html" <span class="green">200</span></div>
|
||
<div><span class="time">02:30:07</span> "GET /runs/latest.json" <span class="green">200</span></div>
|
||
<div><span class="time">02:30:18</span> "GET /favicon.ico" <span class="red">404</span></div>
|
||
<div><span class="time">02:30:42</span> "GET /qmp/report.html" <span class="green">200</span></div>
|
||
<div><span class="time">02:31:09</span> "GET /runs/latest.json" <span class="green">200</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
<figcaption>
|
||
The whole herd in one terminal. Click the sidebar, it's a real layout.
|
||
</figcaption>
|
||
</figure>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="logo-cloud" aria-labelledby="logo-cloud-title">
|
||
<div class="logo-cloud-heading">
|
||
<p class="section-kicker"># used in the wild</p>
|
||
<h2 id="logo-cloud-title">Popular with engineers from</h2>
|
||
<p class="logo-cloud-note">Individual engineers, not company endorsements.</p>
|
||
</div>
|
||
<div class="logo-marquee" aria-label="Company logos">
|
||
<div class="logo-track">
|
||
<div class="logo-row">
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/jetbrains.svg" alt="JetBrains" /><span>JetBrains</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/docker.svg" alt="Docker" /><span>Docker</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/vercel.svg" alt="Vercel" /><span>Vercel</span></div>
|
||
<div class="logo-card"><img width="94" height="32" src="/assets/company-logos/kimi-with-icon-light.svg" alt="Kimi" /><span>Kimi</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/sentry.svg" alt="Sentry" /><span>Sentry</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/google.svg" alt="Google" /><span>Google</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/nvidia.svg" alt="NVIDIA" /><span>NVIDIA</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/aws.svg" alt="AWS" /><span>AWS</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/bytedance.svg" alt="ByteDance" /><span>ByteDance</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/tencent.svg" alt="Tencent" /><span>Tencent</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/alibaba.svg" alt="Alibaba" /><span>Alibaba</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/salesforce.svg" alt="Salesforce" /><span>Salesforce</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/ibm.svg" alt="IBM" /><span>IBM</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/atlassian.svg" alt="Atlassian" /><span>Atlassian</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/whop.svg?v=2" alt="Whop" /><span>Whop</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/automattic.svg" alt="Automattic" /><span>Automattic</span></div>
|
||
</div>
|
||
<div class="logo-row" aria-hidden="true">
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/jetbrains.svg" alt="" /><span>JetBrains</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/docker.svg" alt="" /><span>Docker</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/vercel.svg" alt="" /><span>Vercel</span></div>
|
||
<div class="logo-card"><img width="94" height="32" src="/assets/company-logos/kimi-with-icon-light.svg" alt="" /><span>Kimi</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/sentry.svg" alt="" /><span>Sentry</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/google.svg" alt="" /><span>Google</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/nvidia.svg" alt="" /><span>NVIDIA</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/aws.svg" alt="" /><span>AWS</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/bytedance.svg" alt="" /><span>ByteDance</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/tencent.svg" alt="" /><span>Tencent</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/alibaba.svg" alt="" /><span>Alibaba</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/salesforce.svg" alt="" /><span>Salesforce</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/ibm.svg" alt="" /><span>IBM</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/atlassian.svg" alt="" /><span>Atlassian</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/whop.svg?v=2" alt="" /><span>Whop</span></div>
|
||
<div class="logo-card"><img width="82" height="22" src="/assets/company-logos/automattic.svg" alt="" /><span>Automattic</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section community-section" id="community" aria-labelledby="community-title">
|
||
<div class="section-heading-wide">
|
||
<div>
|
||
<p class="section-kicker"># from the community</p>
|
||
<h2 id="community-title">Watch Herdr through other developers' workflows.</h2>
|
||
</div>
|
||
<p>
|
||
Independent walkthroughs from Jilles, DevOps Toolbox, and Better Stack. See how Herdr feels as a tmux-style multiplexer for coding agents, remote sessions, and day-to-day agent orchestration.
|
||
</p>
|
||
</div>
|
||
<div class="community-video-grid" aria-label="Community videos about Herdr">
|
||
<a class="community-video-card" href="https://www.youtube.com/watch?v=qnIu-Xu64H0" target="_blank" rel="noopener">
|
||
<span class="community-video-thumb">
|
||
<img
|
||
src="https://i.ytimg.com/vi/qnIu-Xu64H0/maxresdefault.jpg"
|
||
alt="Thumbnail for Jilles' Herdr in about 6 minutes video"
|
||
loading="lazy"
|
||
/>
|
||
<span class="play-badge" aria-hidden="true"></span>
|
||
</span>
|
||
<span class="community-video-copy">
|
||
<span class="community-video-meta">Jilles · 6:37</span>
|
||
<span class="community-video-title">Herdr in about 6 minutes</span>
|
||
<span class="community-video-desc">The fastest practical intro: install, workspaces, agent states, persistence, and the agent skill workflow.</span>
|
||
</span>
|
||
</a>
|
||
<a class="community-video-card" href="https://www.youtube.com/watch?v=5GtkyPvuvbQ" target="_blank" rel="noopener">
|
||
<span class="community-video-thumb">
|
||
<img
|
||
src="https://i.ytimg.com/vi/5GtkyPvuvbQ/maxresdefault.jpg"
|
||
alt="Thumbnail for DevOps Toolbox's This Tmux Rewrite Is Actually Brilliant video"
|
||
loading="lazy"
|
||
/>
|
||
<span class="play-badge" aria-hidden="true"></span>
|
||
</span>
|
||
<span class="community-video-copy">
|
||
<span class="community-video-meta">DevOps Toolbox · 16:40</span>
|
||
<span class="community-video-title">This Tmux "Rewrite" Is Actually Brilliant</span>
|
||
<span class="community-video-desc">A deeper hands-on look at the full package: mouse-first panes, remote use, agent support, and the CLI.</span>
|
||
</span>
|
||
</a>
|
||
<a class="community-video-card" href="https://www.youtube.com/watch?v=PlN86TvzGy4" target="_blank" rel="noopener">
|
||
<span class="community-video-thumb">
|
||
<img
|
||
src="https://i.ytimg.com/vi/PlN86TvzGy4/maxresdefault.jpg"
|
||
alt="Thumbnail for Better Stack's Herdr agent multiplexer video"
|
||
loading="lazy"
|
||
/>
|
||
<span class="play-badge" aria-hidden="true"></span>
|
||
</span>
|
||
<span class="community-video-copy">
|
||
<span class="community-video-meta">Better Stack · 7:10</span>
|
||
<span class="community-video-title">Is This the Ultimate Agent Multiplexer?</span>
|
||
<span class="community-video-desc">A clear tmux comparison with agent awareness, remote attach, and why Herdr stays inside your terminal.</span>
|
||
</span>
|
||
</a>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section access-section why-section" id="why">
|
||
<div class="section-heading-wide">
|
||
<div>
|
||
<p class="section-kicker"># why herdr</p>
|
||
<h2>Run agents where the work is. Attach from anywhere.</h2>
|
||
</div>
|
||
<p>
|
||
Agents run wherever the work is: a server, a Mac Mini,
|
||
a sandbox VM, anywhere you can ssh. You attach from any
|
||
terminal, even a phone. Desktop agent managers can't
|
||
leave the machine with the GUI. Herdr can.
|
||
<a href="/compare/">Why switch from an app →</a>
|
||
</p>
|
||
</div>
|
||
<div class="access-grid">
|
||
<article class="access-card">
|
||
<span class="access-label">local</span>
|
||
<h3>Your machine</h3>
|
||
<p>
|
||
Split panes, create tabs, and keep agents running
|
||
while your terminal comes and goes.
|
||
</p>
|
||
<pre><code>$ herdr</code></pre>
|
||
</article>
|
||
<article class="access-card">
|
||
<span class="access-label">over ssh</span>
|
||
<h3>Like tmux, on the server</h3>
|
||
<p>
|
||
SSH in and run Herdr on the remote shell. The session
|
||
stays after you detach, ideal from a phone SSH client.
|
||
</p>
|
||
<pre><code>$ ssh you@server
|
||
$ herdr</code></pre>
|
||
</article>
|
||
<article class="access-card">
|
||
<span class="access-label">thin client</span>
|
||
<h3>Native remote attach</h3>
|
||
<p>
|
||
A local client to a remote session. Installs Herdr on
|
||
the host for you, bridges your local clipboard
|
||
(including image paste), and keeps your keybindings.
|
||
</p>
|
||
<pre><code>$ herdr --remote workbox</code></pre>
|
||
</article>
|
||
</div>
|
||
<div class="mobile-proof">
|
||
<div class="mobile-copy">
|
||
<p class="section-kicker"># responsive tui</p>
|
||
<h3>Mobile-first when the terminal gets small.</h3>
|
||
<p>
|
||
Herdr stays usable over SSH from a phone or tablet.
|
||
The terminal view remains real, while narrow screens
|
||
get a switcher built for touch-sized decisions.
|
||
</p>
|
||
</div>
|
||
<div class="mobile-shots">
|
||
<figure>
|
||
<img
|
||
src="/assets/mobile-agent-session-v2.jpeg"
|
||
alt="Herdr agent session over SSH on a phone"
|
||
loading="lazy"
|
||
/>
|
||
<figcaption>agent session over SSH</figcaption>
|
||
</figure>
|
||
<figure>
|
||
<img
|
||
src="/assets/mobile-switch-menu-v2.jpeg"
|
||
alt="Herdr responsive switch menu on a phone"
|
||
loading="lazy"
|
||
/>
|
||
<figcaption>responsive switch menu</figcaption>
|
||
</figure>
|
||
<p class="mobile-shout">
|
||
screenshots taken with
|
||
<a href="https://getmoshi.app/" target="_blank" rel="noopener">moshi</a>
|
||
❤️ on iPhone
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section compare-section">
|
||
<div class="section-heading-wide">
|
||
<div>
|
||
<p class="section-kicker"># why different</p>
|
||
<h2>Most agent managers are apps. Herdr is a multiplexer.</h2>
|
||
</div>
|
||
<p>
|
||
tmux and Zellij own persistent terminal sessions but don't understand agents. Desktop agent apps understand agents but live on one machine. Herdr keeps the multiplexer in your terminal and makes it agent-aware.
|
||
<a href="/compare/">See the full comparison →</a>
|
||
</p>
|
||
</div>
|
||
<div class="contrast-grid" aria-label="What Herdr is not">
|
||
<article>
|
||
<span>not a terminal emulator</span>
|
||
<p>Ghostty, Kitty, iTerm, Alacritty: your terminal stays.</p>
|
||
</article>
|
||
<article>
|
||
<span>not a browser dashboard</span>
|
||
<p>No web view, no account, no hosted control plane.</p>
|
||
</article>
|
||
<article>
|
||
<span>more than tmux for agents</span>
|
||
<p>Persistence plus clickable panes, agent state, an API.</p>
|
||
</article>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Capability</th>
|
||
<th>tmux / Zellij</th>
|
||
<th>agent apps</th>
|
||
<th>worktree orchestrators</th>
|
||
<th>Herdr</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Runs inside your terminal</td>
|
||
<td>✓</td>
|
||
<td>—</td>
|
||
<td>—</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Persistent PTY sessions</td>
|
||
<td>✓</td>
|
||
<td>limited</td>
|
||
<td>embedded</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Remote SSH attach</td>
|
||
<td>✓</td>
|
||
<td>limited</td>
|
||
<td>remote projects</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Semantic agent state</td>
|
||
<td>—</td>
|
||
<td>partial</td>
|
||
<td>workspace status</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Direct agent attach</td>
|
||
<td>—</td>
|
||
<td>—</td>
|
||
<td>—</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Agents can orchestrate it</td>
|
||
<td>scriptable</td>
|
||
<td>partial</td>
|
||
<td>workflow-owned</td>
|
||
<td>✓</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="compare-cards" aria-label="Mobile comparison">
|
||
<article>
|
||
<h3>Runs inside your terminal</h3>
|
||
<span>tmux/Zellij <b>✓</b></span>
|
||
<span>agent apps <b>—</b></span>
|
||
<span>worktree apps <b>—</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Persistent PTY sessions</h3>
|
||
<span>tmux/Zellij <b>✓</b></span>
|
||
<span>agent apps <b>limited</b></span>
|
||
<span>worktree apps <b>embedded</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Remote SSH attach</h3>
|
||
<span>tmux/Zellij <b>✓</b></span>
|
||
<span>agent apps <b>limited</b></span>
|
||
<span>worktree apps <b>remote projects</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Semantic agent state</h3>
|
||
<span>tmux/Zellij <b>—</b></span>
|
||
<span>agent apps <b>partial</b></span>
|
||
<span>worktree apps <b>workspace status</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Direct agent attach</h3>
|
||
<span>tmux/Zellij <b>—</b></span>
|
||
<span>agent apps <b>—</b></span>
|
||
<span>worktree apps <b>—</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
<article>
|
||
<h3>Agents can orchestrate it</h3>
|
||
<span>tmux/Zellij <b>scriptable</b></span>
|
||
<span>agent apps <b>partial</b></span>
|
||
<span>worktree apps <b>workflow-owned</b></span>
|
||
<span class="herdr">Herdr <b>✓</b></span>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section capability-section">
|
||
<div class="section-heading-wide">
|
||
<div>
|
||
<p class="section-kicker"># what you get</p>
|
||
<h2>Real panes, agent state, and an API to drive them.</h2>
|
||
</div>
|
||
<p>
|
||
No rebuilt chat view. Real processes in real PTYs, with
|
||
clickable layout, persistent sessions, and a control
|
||
surface your agents can use themselves.
|
||
</p>
|
||
</div>
|
||
<div class="capability-body">
|
||
<div class="capability-cards">
|
||
<article>
|
||
<h3>Real panes</h3>
|
||
<p>Mouse-first panes, tabs, and workspaces. Keep your shell, fonts, and keybinds.</p>
|
||
</article>
|
||
<article>
|
||
<h3>Agent state</h3>
|
||
<p>Blocked, working, done, and idle at a glance across the whole session.</p>
|
||
</article>
|
||
<article>
|
||
<h3>Persistence</h3>
|
||
<p>Detach and reattach. Sessions and agents survive the terminal closing.</p>
|
||
</article>
|
||
<article>
|
||
<h3>Control surface</h3>
|
||
<p>A CLI and JSON socket API expose workspaces, panes, output, and waits.</p>
|
||
</article>
|
||
</div>
|
||
<div class="code-card" aria-label="Herdr CLI orchestration example">
|
||
<pre><code><span class="comment"># create workspace structure</span>
|
||
<span class="cmd">herdr</span> workspace create <span class="flag">--cwd</span> <span class="str">~/project</span> <span class="flag">--label</span> <span class="str">api</span>
|
||
<span class="cmd">herdr</span> tab create <span class="flag">--label</span> <span class="str">logs</span>
|
||
|
||
<span class="comment"># split a pane and run work</span>
|
||
<span class="cmd">herdr</span> pane split <span class="str">1-1</span> <span class="flag">--direction</span> <span class="str">right</span>
|
||
<span class="cmd">herdr</span> pane run <span class="str">1-2</span> <span class="str">"just test"</span>
|
||
|
||
<span class="comment"># wait, inspect, and continue</span>
|
||
<span class="cmd">herdr</span> wait agent-status <span class="str">1-1</span> <span class="flag">--status</span> <span class="str">done</span>
|
||
<span class="cmd">herdr</span> pane read <span class="str">1-2</span> <span class="flag">--source</span> <span class="str">recent-unwrapped</span></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="inline-links">
|
||
<a href="/docs/cli-reference/">CLI reference →</a>
|
||
<a href="/docs/socket-api/">Socket API →</a>
|
||
<a href="/docs/agent-skill/">Agent skill →</a>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section integrations-section">
|
||
<div class="section-heading">
|
||
<p class="section-kicker"># integrations</p>
|
||
<h2>Bring your existing agents into the herd.</h2>
|
||
</div>
|
||
<div class="agent-grid" aria-label="Supported agents">
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/pi.svg')"></span><span>Pi</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/claude.svg')"></span><span>Claude Code</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/codex-mask.svg')"></span><span>Codex</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/droid.png')"></span><span>Droid</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/amp.svg')"></span><span>Amp</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/opencode.svg')"></span><span>OpenCode</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/grok.svg')"></span><span>Grok CLI</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/hermes.svg')"></span><span>Hermes</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/cursor.svg')"></span><span>Cursor</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/antigravity.svg')"></span><span>Antigravity</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/kimi.svg')"></span><span>Kimi</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/kiro-mask.svg')"></span><span>Kiro</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/github-copilot.svg')"></span><span>Copilot</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/qoder-mask.svg')"></span><span>Qoder CLI</span></div>
|
||
<div class="agent-card"><span class="agent-icon" style="--agent-icon: url('/assets/agent-icons/mastracode.svg')"></span><span>MastraCode</span></div>
|
||
<a class="agent-card agent-more" href="/docs/integrations/"><span>+ yours</span></a>
|
||
</div>
|
||
<p class="integration-note">
|
||
Any terminal agent works out of the box.
|
||
<a href="/docs/integrations/">Integrations</a> add richer
|
||
state and session resume.
|
||
</p>
|
||
</section>
|
||
|
||
<section class="section marketplace-teaser" id="marketplace">
|
||
<div class="marketplace-teaser-copy">
|
||
<p class="section-kicker"># marketplace</p>
|
||
<h2>Built on, not just with.</h2>
|
||
<p class="marketplace-teaser-lede">
|
||
Herdr's CLI and socket API are the plugin surface, so
|
||
community plugins drive it like an agent does: notifiers,
|
||
layout presets, link handlers, release checks. Browse what
|
||
people built, or publish your own with one GitHub topic.
|
||
</p>
|
||
<div class="hero-actions">
|
||
<a class="button button-primary" href="/plugins/">Browse plugins</a>
|
||
<a class="button button-secondary" href="/docs/plugins/">Build a plugin</a>
|
||
</div>
|
||
</div>
|
||
<div class="marketplace-teaser-grid">
|
||
<a class="mp-chip" href="https://github.com/ogulcancelik/herdr-plugin-examples/tree/main/agent-telegram-notify" target="_blank" rel="noopener">
|
||
<span class="mp-chip-name">Agent Telegram Notify</span>
|
||
<span class="mp-chip-desc">Telegram message when an agent finishes.</span>
|
||
</a>
|
||
<a class="mp-chip" href="https://github.com/ogulcancelik/herdr-plugin-examples/tree/main/github-link-preview" target="_blank" rel="noopener">
|
||
<span class="mp-chip-name">GitHub Link Preview</span>
|
||
<span class="mp-chip-desc">Open clicked issue and PR links in a side pane.</span>
|
||
</a>
|
||
<a class="mp-chip" href="https://github.com/ogulcancelik/herdr-plugin-examples/tree/main/dev-layout-bootstrap" target="_blank" rel="noopener">
|
||
<span class="mp-chip-name">Dev Layout Bootstrap</span>
|
||
<span class="mp-chip-desc">A three-pane dev layout around the current pane.</span>
|
||
</a>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="final-cta">
|
||
<p class="section-kicker"># start the herd</p>
|
||
<h2>Install Herdr, run two agents, detach once.</h2>
|
||
<p>
|
||
The value shows up the first time the herd keeps running
|
||
after your terminal disappears.
|
||
</p>
|
||
<div class="hero-actions center">
|
||
<a class="button button-primary" href="/docs/install/"
|
||
>Install Herdr</a
|
||
>
|
||
<a class="button button-secondary" href="/docs/quick-start/"
|
||
>Quick start</a
|
||
>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<footer class="footer">
|
||
<a class="brand" href="/" aria-label="Herdr home">
|
||
<span class="brand-logo" aria-hidden="true"></span>
|
||
<span>herdr</span>
|
||
</a>
|
||
<div>
|
||
<a href="/compare/">Compare</a>
|
||
<a href="/docs/">Docs</a>
|
||
<a href="/docs/configuration/">Configuration</a>
|
||
<a href="/docs/socket-api/">API</a>
|
||
<a href="https://github.com/ogulcancelik/herdr/releases"
|
||
>Releases</a
|
||
>
|
||
<a href="mailto:hey@herdr.dev">hey@herdr.dev</a>
|
||
</div>
|
||
<nav class="footer-social" aria-label="Social links">
|
||
<a href="https://github.com/ogulcancelik/herdr" aria-label="Herdr on GitHub">
|
||
<svg viewBox="0 0 16 16" aria-hidden="true">
|
||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82A7.64 7.64 0 0 1 8 3.87c.68 0 1.36.09 2 .26 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" />
|
||
</svg>
|
||
</a>
|
||
<a href="https://x.com/herdrdev" aria-label="Herdr on X">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||
<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>
|
||
</nav>
|
||
</footer>
|
||
|
||
<div class="logo-menu" id="logo-menu" hidden>
|
||
<a href="/assets/logo.svg" download="herdr-logo.svg">Download SVG</a>
|
||
<a href="/assets/logo.png" download="herdr-logo.png">Download PNG</a>
|
||
<button type="button" id="copy-logo-url">Copy SVG URL</button>
|
||
</div>
|
||
|
||
<div class="theme-hint" id="theme-hint" title="Press / or t to switch theme">
|
||
<span class="theme-hint-dot"></span>
|
||
<span class="theme-hint-label" id="theme-hint-label">light</span>
|
||
<span class="theme-hint-key">[/]</span>
|
||
</div>
|
||
|
||
<div class="theme-overlay" id="theme-overlay" hidden>
|
||
<div class="theme-overlay-panel" role="dialog" aria-label="Theme switcher" aria-modal="true">
|
||
<div class="theme-overlay-title">settings</div>
|
||
<div class="theme-overlay-tabs">
|
||
<span class="active">theme</span>
|
||
</div>
|
||
<div class="theme-overlay-list" id="theme-overlay-list" role="listbox" aria-label="Themes">
|
||
<div class="theme-sep"><span>dark</span></div>
|
||
<button type="button" role="option" data-value="catppuccin">catppuccin</button>
|
||
<button type="button" role="option" data-value="terminal">terminal</button>
|
||
<button type="button" role="option" data-value="tokyo-night">tokyo night</button>
|
||
<button type="button" role="option" data-value="dracula">dracula</button>
|
||
<button type="button" role="option" data-value="nord">nord</button>
|
||
<button type="button" role="option" data-value="gruvbox">gruvbox</button>
|
||
<button type="button" role="option" data-value="one-dark">one dark</button>
|
||
<button type="button" role="option" data-value="solarized">solarized</button>
|
||
<button type="button" role="option" data-value="kanagawa">kanagawa</button>
|
||
<button type="button" role="option" data-value="rose-pine">rose pine</button>
|
||
<button type="button" role="option" data-value="vesper">vesper</button>
|
||
<div class="theme-sep"><span>light</span></div>
|
||
<button type="button" role="option" data-value="catppuccin-latte">catppuccin latte</button>
|
||
<button type="button" role="option" data-value="tokyo-night-day">tokyo day</button>
|
||
<button type="button" role="option" data-value="gruvbox-light">gruvbox light</button>
|
||
<button type="button" role="option" data-value="one-light">one light</button>
|
||
<button type="button" role="option" data-value="solarized-light">solarized light</button>
|
||
<button type="button" role="option" data-value="kanagawa-lotus">kanagawa lotus</button>
|
||
<button type="button" role="option" data-value="rose-pine-dawn">rose pine dawn</button>
|
||
</div>
|
||
<div class="theme-overlay-footer">
|
||
<span class="theme-overlay-hints"><span class="key">↑↓</span> select</span>
|
||
<span class="theme-overlay-actions"><span class="theme-apply-btn">↵ apply</span><button class="theme-close-btn" type="button" id="theme-close-btn">esc close</button></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
var overlay = document.getElementById("theme-overlay");
|
||
var list = document.getElementById("theme-overlay-list");
|
||
var hintLabel = document.getElementById("theme-hint-label");
|
||
if (!overlay || !list) return;
|
||
|
||
var options = Array.prototype.slice.call(list.querySelectorAll("[role='option']"));
|
||
var media = window.matchMedia("(prefers-color-scheme: dark)");
|
||
var saved = localStorage.getItem("herdr-palette") || "";
|
||
var committed = saved || defaultPalette();
|
||
|
||
function defaultPalette() {
|
||
return media.matches ? "tokyo-night" : "tokyo-night-day";
|
||
}
|
||
|
||
function setPalette(value) {
|
||
document.documentElement.setAttribute("data-palette", value || defaultPalette());
|
||
}
|
||
|
||
function updateUI(value) {
|
||
var activeValue = value || defaultPalette();
|
||
options.forEach(function (opt) {
|
||
var selected = opt.dataset.value === activeValue;
|
||
opt.setAttribute("aria-selected", String(selected));
|
||
});
|
||
var active = options.find(function (o) { return o.dataset.value === activeValue; });
|
||
if (hintLabel && active) hintLabel.textContent = active.textContent.trim();
|
||
}
|
||
|
||
function openOverlay() {
|
||
overlay.hidden = false;
|
||
document.body.style.overflow = "hidden";
|
||
committed = localStorage.getItem("herdr-palette") || defaultPalette();
|
||
updateUI(committed);
|
||
var active = list.querySelector("[aria-selected='true']") || options[0];
|
||
if (active) active.focus();
|
||
}
|
||
|
||
function closeOverlay() {
|
||
overlay.hidden = true;
|
||
document.body.style.overflow = "";
|
||
}
|
||
|
||
function toggleOverlay() {
|
||
if (overlay.hidden) openOverlay();
|
||
else closeOverlay();
|
||
}
|
||
|
||
function preview(index) {
|
||
if (index < 0 || index >= options.length) return;
|
||
setPalette(options[index].dataset.value);
|
||
updateUI(options[index].dataset.value);
|
||
options[index].focus();
|
||
}
|
||
|
||
function commit(value) {
|
||
setPalette(value);
|
||
localStorage.setItem("herdr-palette", value);
|
||
updateUI(value);
|
||
closeOverlay();
|
||
}
|
||
|
||
function revert() {
|
||
setPalette(committed);
|
||
updateUI(committed);
|
||
closeOverlay();
|
||
}
|
||
|
||
media.addEventListener("change", function () {
|
||
if (!localStorage.getItem("herdr-palette")) {
|
||
setPalette("");
|
||
updateUI("");
|
||
}
|
||
});
|
||
|
||
options.forEach(function (opt, idx) {
|
||
opt.addEventListener("click", function () {
|
||
commit(opt.dataset.value);
|
||
});
|
||
opt.addEventListener("mouseenter", function () {
|
||
preview(idx);
|
||
});
|
||
});
|
||
|
||
document.addEventListener("keydown", function (e) {
|
||
if ((e.key === "t" || e.key === "/") && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||
var tag = document.activeElement && document.activeElement.tagName;
|
||
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
||
e.preventDefault();
|
||
toggleOverlay();
|
||
return;
|
||
}
|
||
if (overlay.hidden) return;
|
||
var current = document.activeElement;
|
||
var idx = options.indexOf(current);
|
||
if (e.key === "ArrowDown") {
|
||
e.preventDefault();
|
||
var nextIdx = idx >= 0 ? Math.min(options.length - 1, idx + 1) : 0;
|
||
preview(nextIdx);
|
||
} else if (e.key === "ArrowUp") {
|
||
e.preventDefault();
|
||
var prevIdx = idx >= 0 ? Math.max(0, idx - 1) : options.length - 1;
|
||
preview(prevIdx);
|
||
} else if (e.key === "Enter" && idx >= 0) {
|
||
e.preventDefault();
|
||
commit(options[idx].dataset.value);
|
||
} else if (e.key === "Escape") {
|
||
e.preventDefault();
|
||
revert();
|
||
}
|
||
});
|
||
|
||
var themeHint = document.getElementById("theme-hint");
|
||
if (localStorage.getItem("herdr-theme-hint-seen") === "1") {
|
||
themeHint.classList.add("is-seen");
|
||
}
|
||
themeHint.addEventListener("mouseenter", function () {
|
||
localStorage.setItem("herdr-theme-hint-seen", "1");
|
||
themeHint.classList.add("is-seen");
|
||
}, { once: true });
|
||
themeHint.addEventListener("click", toggleOverlay);
|
||
document.getElementById("theme-close-btn").addEventListener("click", revert);
|
||
|
||
setPalette(saved);
|
||
updateUI(saved);
|
||
})();
|
||
|
||
(function () {
|
||
var menu = document.getElementById("logo-menu");
|
||
var copyButton = document.getElementById("copy-logo-url");
|
||
var logos = document.querySelectorAll(".brand-logo");
|
||
if (!menu || !logos.length) return;
|
||
|
||
function hideMenu() {
|
||
menu.hidden = true;
|
||
}
|
||
|
||
function showMenu(event) {
|
||
event.preventDefault();
|
||
menu.hidden = false;
|
||
var menuWidth = menu.offsetWidth;
|
||
var menuHeight = menu.offsetHeight;
|
||
var left = Math.min(event.clientX, window.innerWidth - menuWidth - 12);
|
||
var top = Math.min(event.clientY, window.innerHeight - menuHeight - 12);
|
||
menu.style.left = Math.max(12, left) + "px";
|
||
menu.style.top = Math.max(12, top) + "px";
|
||
}
|
||
|
||
logos.forEach(function (logo) {
|
||
logo.addEventListener("contextmenu", showMenu);
|
||
});
|
||
|
||
document.addEventListener("click", function (event) {
|
||
if (!menu.contains(event.target)) hideMenu();
|
||
});
|
||
document.addEventListener("keydown", function (event) {
|
||
if (event.key === "Escape") hideMenu();
|
||
});
|
||
window.addEventListener("scroll", hideMenu, { passive: true });
|
||
window.addEventListener("resize", hideMenu);
|
||
|
||
if (copyButton) {
|
||
copyButton.addEventListener("click", async function () {
|
||
var url = new URL("/assets/logo.svg", window.location.href).href;
|
||
try {
|
||
await navigator.clipboard.writeText(url);
|
||
copyButton.textContent = "Copied";
|
||
setTimeout(function () {
|
||
copyButton.textContent = "Copy SVG URL";
|
||
}, 1500);
|
||
} catch (error) {
|
||
window.prompt("Copy logo URL", url);
|
||
}
|
||
});
|
||
}
|
||
})();
|
||
|
||
(function () {
|
||
var reduceMotion = window.matchMedia(
|
||
"(prefers-reduced-motion: reduce)",
|
||
).matches;
|
||
if (reduceMotion) return;
|
||
|
||
document.querySelectorAll(".logo-marquee").forEach(function (marquee) {
|
||
var track = marquee.querySelector(".logo-track");
|
||
if (!track || !track.getAnimations) return;
|
||
|
||
function setPlaybackRate(rate) {
|
||
var animation = track.getAnimations().find(function (candidate) {
|
||
return candidate.animationName === "logo-marquee";
|
||
});
|
||
if (!animation) return;
|
||
|
||
if (animation.updatePlaybackRate) {
|
||
animation.updatePlaybackRate(rate);
|
||
} else {
|
||
animation.playbackRate = rate;
|
||
}
|
||
}
|
||
|
||
marquee.addEventListener("mouseenter", function () {
|
||
setPlaybackRate(0.6);
|
||
});
|
||
marquee.addEventListener("mouseleave", function () {
|
||
setPlaybackRate(1);
|
||
});
|
||
});
|
||
})();
|
||
|
||
(function () {
|
||
var copyButton = document.getElementById("copy-btn");
|
||
var command = document.getElementById("install-cmd");
|
||
var methods = Array.prototype.slice.call(
|
||
document.querySelectorAll(".install-method"),
|
||
);
|
||
if (!copyButton || !command || methods.length === 0) return;
|
||
|
||
methods.forEach(function (method) {
|
||
method.addEventListener("click", function () {
|
||
methods.forEach(function (entry) {
|
||
var active = entry === method;
|
||
entry.classList.toggle("is-active", active);
|
||
entry.setAttribute("aria-pressed", String(active));
|
||
});
|
||
command.textContent = method.dataset.command;
|
||
command.classList.toggle(
|
||
"is-compact",
|
||
method.dataset.size === "compact",
|
||
);
|
||
copyButton.textContent = "Copy";
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll("[data-copy-target]").forEach(function (button) {
|
||
button.addEventListener("click", async function () {
|
||
var target = document.getElementById(button.dataset.copyTarget);
|
||
if (!target) return;
|
||
|
||
try {
|
||
await navigator.clipboard.writeText(
|
||
target.textContent.trim(),
|
||
);
|
||
button.textContent = "Copied";
|
||
setTimeout(function () {
|
||
button.textContent = "Copy";
|
||
}, 1500);
|
||
} catch (error) {
|
||
button.textContent = "Select";
|
||
}
|
||
});
|
||
});
|
||
})();
|
||
|
||
(function () {
|
||
var frames = ["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];
|
||
var tick = 0;
|
||
setInterval(function () {
|
||
tick = (tick + 1) % frames.length;
|
||
var els = document.querySelectorAll(".braille-spinner");
|
||
for (var i = 0; i < els.length; i++) {
|
||
els[i].textContent = frames[tick];
|
||
}
|
||
}, 80);
|
||
})();
|
||
|
||
/* herd mock: clickable workspaces, tabs, and agents */
|
||
(function () {
|
||
var mock = document.getElementById("herd-mock");
|
||
if (!mock) return;
|
||
|
||
function selectTab(tab) {
|
||
var target = tab.getAttribute("data-tab");
|
||
if (!target) return;
|
||
tab.parentElement.querySelectorAll("span").forEach(function (t) {
|
||
t.classList.remove("active");
|
||
});
|
||
tab.classList.add("active");
|
||
var view = tab.closest(".mock-view");
|
||
view.querySelectorAll(".mock-panes").forEach(function (panes) {
|
||
panes.classList.toggle("active", panes.getAttribute("data-tab") === target);
|
||
});
|
||
}
|
||
|
||
function activateWorkspace(id, tabId) {
|
||
mock.querySelectorAll(".mock-space").forEach(function (ws) {
|
||
ws.classList.toggle("active", ws.getAttribute("data-ws") === id);
|
||
});
|
||
mock.querySelectorAll(".mock-view").forEach(function (view) {
|
||
view.classList.toggle("active", view.getAttribute("data-view") === id);
|
||
});
|
||
if (tabId) {
|
||
var view = mock.querySelector('.mock-view[data-view="' + id + '"]');
|
||
var tab = view && view.querySelector('.mock-tabs span[data-tab="' + tabId + '"]');
|
||
if (tab) selectTab(tab);
|
||
}
|
||
}
|
||
|
||
mock.querySelectorAll(".mock-space").forEach(function (ws) {
|
||
ws.addEventListener("click", function () {
|
||
activateWorkspace(ws.getAttribute("data-ws"));
|
||
});
|
||
});
|
||
|
||
mock.querySelectorAll(".mock-agent").forEach(function (a) {
|
||
a.addEventListener("click", function () {
|
||
activateWorkspace(
|
||
a.getAttribute("data-ws-link"),
|
||
a.getAttribute("data-tab-link")
|
||
);
|
||
});
|
||
});
|
||
|
||
mock.querySelectorAll(".mock-tabs span").forEach(function (tab) {
|
||
if (!tab.getAttribute("data-tab")) return;
|
||
tab.addEventListener("click", function (event) {
|
||
event.stopPropagation();
|
||
selectTab(tab);
|
||
});
|
||
});
|
||
|
||
/* the working counter keeps counting, like the real thing */
|
||
var timer = document.getElementById("cc-timer");
|
||
if (timer) {
|
||
var seconds = 13 * 60 + 36;
|
||
setInterval(function () {
|
||
if (document.hidden) return;
|
||
seconds += 1;
|
||
if (seconds >= 16 * 60) seconds = 0; /* new turn, counter resets */
|
||
timer.textContent = Math.floor(seconds / 60) + "m " + (seconds % 60) + "s";
|
||
}, 1000);
|
||
}
|
||
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|