herdr/website/index.html

793 lines
33 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>herdr — tmux for agents</title>
<meta
name="description"
content="Persistent terminal multiplexer for coding agents. Detach and reattach, agents keep running. Built-in awareness for blocked, done, and working states. Lives inside your terminal."
/>
<meta name="theme-color" content="#11111b" />
<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 — tmux for agents" />
<meta
property="og:description"
content="Persistent terminal multiplexer for coding agents. Detach, reattach, agents keep running."
/>
<meta property="og:url" content="https://herdr.dev/" />
<meta
property="og:image"
content="https://herdr.dev/assets/og-card-v2.png"
/>
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="herdr — tmux for agents" />
<meta
name="twitter:description"
content="Persistent terminal multiplexer for coding agents. Detach, reattach, agents keep running."
/>
<meta
name="twitter:image"
content="https://herdr.dev/assets/og-card-v2.png"
/>
<link rel="icon" type="image/png" href="/assets/logo.png" />
<link rel="apple-touch-icon" href="/assets/logo.png" />
<link
rel="preload"
href="/assets/jbm.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link rel="stylesheet" href="/css/style.css?v=d0a150a" />
</head>
<body>
<!-- ── Hero ── -->
<section class="hero" id="hero">
<div class="hero-pasture" aria-hidden="true"></div>
<img src="/assets/logo.png" alt="herdr" class="hero-logo" />
<span class="hero-name">herdr</span>
<p class="hero-tagline">tmux for agents.</p>
<div class="hero-dots">
<span class="dot dot-blocked"></span>
<span class="dot dot-working"></span>
<span class="dot dot-done"></span>
<span class="dot dot-idle"></span>
</div>
<div class="manifesto">
<p>
<strong
>herdr is an agent multiplexer that lives in your
terminal.</strong
>
</p>
<p>
workspaces, tabs, panes. mouse-native: click, drag, split.
every agent at a glance:
<span class="text-blocked">blocked</span>,
<span class="text-working">working</span>,
<span class="text-done">done</span>. detach and reattach,
agents keep running. attach locally, over ssh, or as a thin
client to a remote server. no gui app, no electron, no
mac-only native wrapper. you see the agent's own terminal,
not someone's interpretation of it.
</p>
</div>
<div class="install-row">
<div class="install-box">
<span class="prompt">$</span>
<code id="install-cmd"
>curl -fsSL https://herdr.dev/install.sh | sh</code
>
</div>
<button class="copy-btn" id="copy-btn">copy</button>
</div>
<div class="hero-meta">
<a href="https://github.com/ogulcancelik/herdr">github</a> ·
<a
href="https://github.com/ogulcancelik/herdr/blob/master/CONFIGURATION.md"
>docs</a
>
·
<a
href="https://github.com/ogulcancelik/herdr/blob/master/SOCKET_API.md"
>api</a
>
· linux · macos · agpl-3.0
</div>
</section>
<div class="gap-lg"></div>
<!-- ── Demo ── -->
<div class="demo">
<div class="demo-frame">
<video
autoplay
muted
loop
playsinline
preload="auto"
poster="/assets/hero-screenshot.png"
>
<source src="/assets/demo.mp4" type="video/mp4" />
</video>
</div>
</div>
<div class="gap-lg"></div>
<hr class="section-rule" />
<div class="gap"></div>
<!-- ── Comparison ── -->
<section class="section">
<div class="section-heading">
<span class="dot dot-idle"></span>
<h2>how it compares</h2>
</div>
<div class="table-wrap">
<table class="position-table">
<thead>
<tr>
<th></th>
<th>tmux</th>
<th>gui managers</th>
<th class="herdr-col">herdr</th>
</tr>
</thead>
<tbody>
<tr>
<td>persistent sessions</td>
<td><span class="mark-yes"></span></td>
<td><span class="mark"></span></td>
<td class="herdr-col">
<span class="mark-yes"></span>
</td>
</tr>
<tr>
<td>detach / reattach</td>
<td><span class="mark-yes"></span></td>
<td><span class="mark"></span></td>
<td class="herdr-col">
<span class="mark-yes"></span>
</td>
</tr>
<tr>
<td>panes, tabs, workspaces</td>
<td><span class="mark-yes"></span></td>
<td><span class="mark-yes"></span></td>
<td class="herdr-col">
<span class="mark-yes"></span>
</td>
</tr>
<tr>
<td>agent awareness</td>
<td><span class="mark"></span></td>
<td><span class="mark-yes"></span></td>
<td class="herdr-col">
<span class="mark-yes"></span>
</td>
</tr>
<tr>
<td>lives in your terminal</td>
<td><span class="mark-yes"></span></td>
<td><span class="mark"></span></td>
<td class="herdr-col">
<span class="mark-yes"></span>
</td>
</tr>
<tr>
<td>real terminal views</td>
<td><span class="mark-yes"></span></td>
<td><span class="mark"></span></td>
<td class="herdr-col">
<span class="mark-yes"></span>
</td>
</tr>
<tr>
<td>mouse-native</td>
<td><span class="mark"></span></td>
<td><span class="mark-yes"></span></td>
<td class="herdr-col">
<span class="mark-yes"></span>
</td>
</tr>
<tr>
<td>lightweight binary</td>
<td><span class="mark-yes"></span></td>
<td><span class="mark"></span></td>
<td class="herdr-col">
<span class="mark-yes"></span>
</td>
</tr>
<tr>
<td>agents can orchestrate</td>
<td><span class="mark">?</span></td>
<td><span class="mark">?</span></td>
<td class="herdr-col">
<span class="mark-yes"></span>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<div class="gap"></div>
<hr class="section-rule" />
<div class="gap"></div>
<!-- ── Persistence ── -->
<section class="section">
<div class="section-heading">
<span class="dot dot-done"></span>
<h2>persistence</h2>
</div>
<div class="prose-block">
<p>
herdr runs as a background session server by default. run
your agents, split panes, do your work, then press
<strong>ctrl+b q</strong> to detach the client. close your
terminal or laptop; the panes keep running. open herdr again
and you reattach to the same session.
</p>
<p>
named sessions give each project or context its own panes,
tabs, workspaces, sockets, and runtime state while sharing
the same global config.
</p>
</div>
<div class="detach-flow" style="margin-top: 1.5rem">
<div class="detach-line">
<span class="prompt">$</span> <span class="cmd">herdr</span>
<span class="key">--session</span>
<span class="comment">work</span>
</div>
<div class="detach-line">
<span class="prompt">$</span> <span class="cmd">herdr</span>
<span class="key">session attach</span>
<span class="comment">side-project</span>
</div>
<div class="detach-line">
<span class="dim">
separate sessions, same config, persistent agents.</span
>
</div>
</div>
</section>
<div class="gap"></div>
<hr class="section-rule" />
<div class="gap"></div>
<!-- ── From anywhere ── -->
<section class="section">
<div class="section-heading">
<span class="dot dot-done"></span>
<h2>from anywhere</h2>
</div>
<div class="prose-block">
<p>
once the session is running, attach to it three ways. keep
the work on the machine that has the code, the keys, and the
agents. connect from wherever you are.
</p>
</div>
<div class="access-grid">
<div class="access-card">
<div class="access-kicker">server</div>
<h3>ssh in, run herdr</h3>
<p>
use it like tmux on a remote box. herdr starts the
session there, and your panes keep running after you
detach.
</p>
<div class="mini-flow">
<div>
<span class="prompt">$</span>
<span class="cmd">ssh</span>
<span class="comment">you@server</span>
</div>
<div>
<span class="prompt">$</span>
<span class="cmd">herdr</span>
</div>
</div>
</div>
<div class="access-card">
<div class="access-kicker">phone</div>
<h3>ssh from mobile</h3>
<p>
narrow terminals get a responsive tui instead of a
squeezed desktop layout. spaces, tabs, and agents stay
reachable from a phone ssh client.
</p>
<div class="mini-flow">
<div>
<span class="prompt">$</span>
<span class="cmd">ssh</span>
<span class="comment">you@server</span>
</div>
<div>
<span class="prompt">$</span>
<span class="cmd">herdr</span>
</div>
</div>
</div>
<div class="access-card">
<div class="access-kicker">thin client</div>
<h3>herdr --remote host</h3>
<p>
run herdr on your laptop and target any ssh config host.
herdr connects over ssh, bootstraps the remote server,
and streams the tui back to your local terminal.
</p>
<div class="mini-flow">
<div>
<span class="prompt">$</span>
<span class="cmd">herdr</span>
<span class="key">--remote</span>
<span class="comment">workbox</span>
</div>
</div>
</div>
</div>
<div class="mobile-proof">
<figure class="phone-shot">
<div class="phone-shot-frame">
<img
src="/assets/mobile-terminal.jpeg"
alt="herdr responsive terminal view over ssh on a phone"
loading="lazy"
/>
</div>
<figcaption>terminal view over ssh</figcaption>
</figure>
<figure class="phone-shot">
<div class="phone-shot-frame">
<img
src="/assets/mobile-switcher.jpeg"
alt="herdr mobile switcher showing spaces tabs and agents on a phone"
loading="lazy"
/>
</div>
<figcaption>responsive switcher</figcaption>
</figure>
</div>
</section>
<div class="gap"></div>
<hr class="section-rule" />
<div class="gap"></div>
<!-- ── Awareness ── -->
<section class="section">
<div class="section-heading">
<span class="dot dot-working"></span>
<h2>agent awareness</h2>
</div>
<div class="awareness-split">
<div class="awareness-panel">
<div class="awareness-panel-img">
<img
src="/assets/sidebar-workspaces.png"
alt="workspaces"
/>
</div>
<div class="awareness-panel-text">
<h3>workspaces</h3>
<p>
each workspace rolls up to its most urgent state.
scan the full list in a glance.
</p>
</div>
</div>
<div class="awareness-panel">
<div class="awareness-panel-img">
<img src="/assets/sidebar-agents.png" alt="agents" />
</div>
<div class="awareness-panel-text">
<h3>agents</h3>
<p>
see which agents are running, blocked, or finished.
no checking panes one by one.
</p>
</div>
</div>
</div>
<div class="state-bar">
<div class="state-bar-item">
<span class="dot dot-blocked"></span> blocked
</div>
<div class="state-bar-item">
<span class="dot dot-working"></span> working
</div>
<div class="state-bar-item">
<span class="dot dot-done"></span> done
</div>
<div class="state-bar-item">
<span class="dot dot-idle"></span> idle
</div>
</div>
</section>
<div class="gap"></div>
<hr class="section-rule" />
<div class="gap"></div>
<!-- ── Terminal ── -->
<section class="section">
<div class="section-heading">
<span class="dot dot-idle"></span>
<h2>lives in your terminal</h2>
</div>
<div class="terminal-block">
<p>
not a gui window. not a web dashboard. not electron.
<strong
>herdr runs inside whatever terminal you already
use.</strong
>
a single rust binary, no dependencies, nothing to configure.
it even works inside tmux.
</p>
<div class="term-pills">
<span class="term-pill">ghostty</span>
<span class="term-pill">alacritty</span>
<span class="term-pill">kitty</span>
<span class="term-pill">wezterm</span>
<span class="term-pill">iterm2</span>
<span class="term-pill">tmux</span>
</div>
</div>
</section>
<div class="gap"></div>
<hr class="section-rule" />
<div class="gap"></div>
<!-- ── Agents ── -->
<section class="section">
<div class="section-heading">
<span class="dot dot-blocked"></span>
<h2>supported agents</h2>
</div>
<div class="table-wrap">
<table class="agents-table">
<thead>
<tr>
<th>agent</th>
<th>idle / done</th>
<th>working</th>
<th>blocked</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://pi.dev">pi</a></td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
<td><span class="partial">partial</span></td>
</tr>
<tr>
<td>
<a
href="https://docs.anthropic.com/en/docs/claude-code"
>claude code</a
>
</td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
</tr>
<tr>
<td>
<a href="https://github.com/openai/codex"
>codex</a
>
</td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
</tr>
<tr>
<td><a href="https://factory.ai">droid</a></td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
</tr>
<tr>
<td><a href="https://ampcode.com">amp</a></td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
</tr>
<tr>
<td>
<a href="https://github.com/anomalyco/opencode"
>opencode</a
>
</td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
<td><span class="check"></span></td>
</tr>
<tr>
<td>
<a
href="https://github.com/google-gemini/gemini-cli"
>gemini cli</a
>
</td>
<td colspan="3">
<span class="untested"
>detected, not fully tested</span
>
</td>
</tr>
<tr>
<td>
<a href="https://cursor.com/cli"
>cursor agent</a
>
</td>
<td colspan="3">
<span class="untested"
>detected, not fully tested</span
>
</td>
</tr>
<tr>
<td>
<a href="https://github.com/cline/cline"
>cline</a
>
</td>
<td colspan="3">
<span class="untested"
>detected, not fully tested</span
>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<div class="gap"></div>
<hr class="section-rule" />
<div class="gap"></div>
<!-- ── API ── -->
<section class="section">
<div class="section-heading">
<span class="dot dot-done"></span>
<h2>socket api</h2>
</div>
<div class="api-grid">
<div class="api-text">
<p>
<strong>agents can use herdr too.</strong> the local
unix socket lets agents create workspaces, split panes,
spawn other agents, read output, and wait for state
changes.
</p>
<p>
available as CLI commands or as a reusable agent skill.
</p>
<div class="api-links">
<a
href="https://github.com/ogulcancelik/herdr/blob/master/SOCKET_API.md"
>socket api →</a
>
<a
href="https://github.com/ogulcancelik/herdr/blob/master/SKILL.md"
>agent skill →</a
>
</div>
</div>
<div class="code-block">
<pre><span class="comment"># create a workspace and tab</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</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">"npm test"</span>
<span class="comment"># wait for an agent</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="comment"># read output</span>
<span class="cmd">herdr</span> pane read <span class="str">1-2</span> <span class="flag">--source</span> <span class="str">recent</span> <span class="flag">--lines</span> <span class="str">50</span></pre>
</div>
</div>
</section>
<div class="gap-lg"></div>
<!-- ── Footer ── -->
<footer class="footer">
<p>
<a href="https://github.com/ogulcancelik/herdr">github</a> ·
<a
href="https://github.com/ogulcancelik/herdr/blob/master/CONFIGURATION.md"
>configuration</a
>
·
<a
href="https://github.com/ogulcancelik/herdr/blob/master/SOCKET_API.md"
>api</a
>
·
<a
href="https://github.com/ogulcancelik/herdr/releases"
>releases</a
>
</p>
</footer>
<script>
(function () {
"use strict";
var hero = document.getElementById("hero");
var pasture = hero.querySelector(".hero-pasture");
var SR = [
[" ,ww", " wWWWWWWW_)", " `WWWWWW'", " II II"],
[" ,ww", " wWWWWWWW_)", " `WWWWWW'", " II II"],
];
var SL = [
[" ww,", "(_WWWWWWWw", " 'WWWWWW`", " II II"],
[" ww,", "(_WWWWWWWw", " 'WWWWWW`", " II II"],
];
var heroW = hero.offsetWidth;
var heroH = hero.offsetHeight;
var heroRect = hero.getBoundingClientRect();
var mx = -9999,
my = -9999;
document.addEventListener("mousemove", function (e) {
mx = e.clientX - heroRect.left;
my = e.clientY - heroRect.top;
});
document.addEventListener("mouseleave", function () {
mx = -9999;
my = -9999;
});
window.addEventListener("resize", function () {
heroW = hero.offsetWidth;
heroH = hero.offsetHeight;
heroRect = hero.getBoundingClientRect();
});
window.addEventListener("scroll", function () {
heroRect = hero.getBoundingClientRect();
});
var flock = [];
var count = window.matchMedia("(max-width: 640px)").matches
? 3
: 5;
for (var i = 0; i < count; i++) {
var el = document.createElement("pre");
el.className = "sheep-el";
var opacity = 0.04 + Math.random() * 0.035;
var size = 9 + Math.random() * 3;
el.style.cssText =
"font-size:" +
size +
"px;color:rgba(203,166,247," +
opacity +
")";
pasture.appendChild(el);
flock.push({
el: el,
x: Math.random() * heroW,
y: 30 + Math.random() * (heroH - 80),
dir: Math.random() > 0.5 ? 1 : -1,
speed: 0.1 + Math.random() * 0.2,
frame: 0,
ft: 0,
fi: 400 + Math.random() * 300,
svx: 0,
svy: 0,
});
}
var last = 0;
function tick(ts) {
var dt = Math.min(ts - last, 100);
last = ts;
for (var i = 0; i < flock.length; i++) {
var s = flock[i];
s.ft += dt;
if (s.ft > s.fi) {
s.ft = 0;
s.frame ^= 1;
}
var dx = s.x - mx,
dy = s.y - my;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < 120 && d > 1) {
var f = (1 - d / 120) * 0.4;
s.svx += (dx / d) * f;
s.svy += (dy / d) * f;
}
s.x += s.svx;
s.y += s.svy;
s.svx *= 0.97;
s.svy *= 0.97;
s.x += s.dir * s.speed * (dt / 16);
if (s.dir > 0 && s.x > heroW + 100) s.x = -100;
if (s.dir < 0 && s.x < -100) s.x = heroW + 100;
if (s.y < -40) s.y = heroH + 20;
if (s.y > heroH + 40) s.y = -20;
var frames = s.dir > 0 ? SR : SL;
s.el.textContent = frames[s.frame].join("\n");
s.el.style.transform =
"translate3d(" + s.x + "px," + s.y + "px,0)";
}
requestAnimationFrame(tick);
}
requestAnimationFrame(function (ts) {
last = ts;
tick(ts);
});
// Copy
var copyBtn = document.getElementById("copy-btn");
var installCmd = document.getElementById("install-cmd");
function fallbackCopy(text) {
var ta = document.createElement("textarea");
ta.value = text;
ta.style.cssText = "position:fixed;left:-9999px";
document.body.appendChild(ta);
ta.select();
document.execCommand("copy");
document.body.removeChild(ta);
}
function showCopied() {
copyBtn.textContent = "copied!";
copyBtn.classList.add("copied");
setTimeout(function () {
copyBtn.textContent = "copy";
copyBtn.classList.remove("copied");
}, 2000);
}
copyBtn.addEventListener("click", function () {
var text = installCmd.textContent;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard
.writeText(text)
.then(showCopied)
.catch(function () {
fallbackCopy(text);
showCopied();
});
} else {
fallbackCopy(text);
showCopied();
}
});
})();
</script>
</body>
</html>