979 lines
33 KiB
HTML
979 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 — herd your agents</title>
|
||
<meta name="description" content="Terminal-native agent multiplexer. Workspaces, tiled panes, agent detection, and a socket API that lets your agents control herdr too.">
|
||
<meta name="theme-color" content="#0d1117">
|
||
<link rel="canonical" href="https://herdr.dev/">
|
||
|
||
<!-- Open Graph -->
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:site_name" content="herdr">
|
||
<meta property="og:title" content="herdr — herd your agents">
|
||
<meta property="og:description" content="Terminal-native agent multiplexer. Workspaces, tiled panes, agent detection, and a socket API that lets your agents control herdr too.">
|
||
<meta property="og:url" content="https://herdr.dev/">
|
||
<meta property="og:image" content="https://herdr.dev/assets/og-card.png">
|
||
<meta property="og:image:width" content="1200">
|
||
<meta property="og:image:height" content="630">
|
||
|
||
<!-- Twitter / X -->
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<meta name="twitter:title" content="herdr — herd your agents">
|
||
<meta name="twitter:description" content="Terminal-native agent multiplexer. Workspaces, tiled panes, agent detection, and a socket API.">
|
||
<meta name="twitter:image" content="https://herdr.dev/assets/og-card.png">
|
||
|
||
<link rel="icon" type="image/png" href="/assets/logo.png">
|
||
<link rel="apple-touch-icon" href="/assets/logo.png">
|
||
<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=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
:root {
|
||
--bg: #0d1117;
|
||
--bg-raised: #161b22;
|
||
--bg-surface: #1c2129;
|
||
--border: #21262d;
|
||
--border-bright: #30363d;
|
||
--text: #e6edf3;
|
||
--text-muted: #8b949e;
|
||
--text-dim: #484f58;
|
||
--green: #4ade80;
|
||
--green-dim: rgba(74, 222, 128, 0.12);
|
||
--red: #f87171;
|
||
--blue: #60a5fa;
|
||
--yellow: #fbbf24;
|
||
--max-w: 1080px;
|
||
}
|
||
|
||
html {
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;
|
||
-webkit-font-smoothing: antialiased;
|
||
scroll-behavior: smooth;
|
||
}
|
||
|
||
body { overflow-x: hidden; }
|
||
|
||
/* ── Hero ── */
|
||
.hero {
|
||
position: relative;
|
||
max-width: var(--max-w);
|
||
margin: 0 auto;
|
||
padding: 4rem 2rem 0;
|
||
text-align: center;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.hero-pasture {
|
||
position: absolute;
|
||
inset: 0;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
|
||
.hero > *:not(.hero-pasture) {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.hero .sheep-el {
|
||
position: absolute;
|
||
font-family: 'JetBrains Mono', monospace;
|
||
line-height: 1.15;
|
||
white-space: pre;
|
||
pointer-events: none;
|
||
user-select: none;
|
||
will-change: transform;
|
||
}
|
||
|
||
.hero-logo {
|
||
width: 80px;
|
||
height: 80px;
|
||
border-radius: 18px;
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
|
||
.hero h1 {
|
||
font-size: 3rem;
|
||
font-weight: 700;
|
||
letter-spacing: -0.03em;
|
||
color: #fff;
|
||
}
|
||
|
||
.hero-tagline {
|
||
font-size: 1.1rem;
|
||
color: var(--green);
|
||
margin: 0.25rem 0 1.25rem;
|
||
}
|
||
|
||
.hero-desc {
|
||
font-size: 0.875rem;
|
||
color: var(--text-muted);
|
||
line-height: 1.8;
|
||
max-width: 640px;
|
||
margin: 0 auto 2rem;
|
||
}
|
||
|
||
.hero-desc p + p { margin-top: 0.6rem; }
|
||
|
||
/* install box */
|
||
.install-box {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
background: var(--bg-raised);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
padding: 0.65rem 1rem;
|
||
font-size: 0.825rem;
|
||
margin-bottom: 0.75rem;
|
||
transition: border-color 0.2s;
|
||
}
|
||
|
||
.install-box:hover { border-color: var(--border-bright); }
|
||
.install-box .prompt { color: var(--green); }
|
||
.install-box code { color: var(--text); }
|
||
|
||
.copy-btn {
|
||
background: none;
|
||
border: 1px solid var(--border);
|
||
color: var(--text-dim);
|
||
font-family: inherit;
|
||
font-size: 0.7rem;
|
||
padding: 0.2rem 0.5rem;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
.copy-btn:hover { border-color: var(--green); color: var(--green); }
|
||
.copy-btn.copied { border-color: var(--green); color: var(--green); }
|
||
|
||
.hero-links {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 1.5rem;
|
||
margin-top: 0.5rem;
|
||
font-size: 0.8rem;
|
||
}
|
||
.hero-links a {
|
||
color: var(--text-dim);
|
||
text-decoration: none;
|
||
transition: color 0.2s;
|
||
}
|
||
.hero-links a:hover { color: var(--green); }
|
||
|
||
/* screenshot */
|
||
.hero-screenshot {
|
||
max-width: var(--max-w);
|
||
margin: 3rem auto 0;
|
||
padding: 0 2rem;
|
||
}
|
||
|
||
.screenshot-frame {
|
||
position: relative;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--border);
|
||
box-shadow:
|
||
0 0 0 1px rgba(0,0,0,0.3),
|
||
0 24px 80px rgba(0,0,0,0.5),
|
||
0 0 120px rgba(74, 222, 128, 0.03);
|
||
}
|
||
|
||
.screenshot-frame img,
|
||
.screenshot-frame video {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
.screenshot-label {
|
||
text-align: center;
|
||
font-size: 0.7rem;
|
||
color: var(--text-dim);
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
/* ── Divider ── */
|
||
.section-gap { height: 5rem; }
|
||
|
||
/* ── Two-column features ── */
|
||
.features {
|
||
max-width: var(--max-w);
|
||
margin: 0 auto;
|
||
padding: 0 2rem;
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 2rem;
|
||
}
|
||
|
||
.feature-card {
|
||
background: var(--bg-raised);
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 2rem;
|
||
}
|
||
|
||
.feature-card h2 {
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
margin-bottom: 0.25rem;
|
||
color: #fff;
|
||
}
|
||
|
||
.feature-card .subtitle {
|
||
font-size: 0.75rem;
|
||
color: var(--green);
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
|
||
.feature-list {
|
||
list-style: none;
|
||
}
|
||
|
||
.feature-list li {
|
||
font-size: 0.8rem;
|
||
color: var(--text-muted);
|
||
line-height: 1.7;
|
||
padding-left: 1.2rem;
|
||
position: relative;
|
||
}
|
||
|
||
.feature-list li::before {
|
||
content: '›';
|
||
position: absolute;
|
||
left: 0;
|
||
color: var(--text-dim);
|
||
}
|
||
|
||
.feature-list li + li { margin-top: 0.4rem; }
|
||
|
||
/* ── Awareness section ── */
|
||
.awareness {
|
||
max-width: var(--max-w);
|
||
margin: 0 auto;
|
||
padding: 0 2rem;
|
||
}
|
||
|
||
.awareness-header {
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.awareness-header h2 {
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
margin-bottom: 0.3rem;
|
||
}
|
||
|
||
.awareness-desc {
|
||
font-size: 0.8rem;
|
||
color: var(--text-muted);
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.awareness-dual {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 2rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.awareness-panel {
|
||
background: var(--bg-raised);
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 1.5rem;
|
||
display: flex;
|
||
gap: 1.25rem;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.awareness-panel-img {
|
||
flex-shrink: 0;
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.awareness-panel-img img {
|
||
display: block;
|
||
height: auto;
|
||
width: auto;
|
||
max-height: 120px;
|
||
image-rendering: auto;
|
||
}
|
||
|
||
.awareness-panel-text h3 {
|
||
font-size: 0.85rem;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
margin-bottom: 0.4rem;
|
||
}
|
||
|
||
.awareness-panel-text p {
|
||
font-size: 0.75rem;
|
||
color: var(--text-muted);
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.state-bar {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 2rem;
|
||
padding: 1rem 0 0;
|
||
}
|
||
|
||
.state-bar-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
font-size: 0.75rem;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.state-dot {
|
||
flex-shrink: 0;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
display: inline-block;
|
||
}
|
||
|
||
.state-dot.red { background: var(--red); box-shadow: 0 0 8px rgba(248,113,113,0.4); }
|
||
.state-dot.blue { background: var(--blue); box-shadow: 0 0 8px rgba(96,165,250,0.4); }
|
||
.state-dot.yellow { background: var(--yellow); box-shadow: 0 0 8px rgba(251,191,36,0.3); }
|
||
.state-dot.green { background: var(--green); opacity: 0.5; }
|
||
|
||
/* ── Agents table ── */
|
||
.agents-section {
|
||
max-width: var(--max-w);
|
||
margin: 0 auto;
|
||
padding: 0 2rem;
|
||
}
|
||
|
||
.agents-section h2 {
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
margin-bottom: 0.3rem;
|
||
}
|
||
|
||
.agents-section .section-desc {
|
||
font-size: 0.775rem;
|
||
color: var(--text-muted);
|
||
margin-bottom: 1.25rem;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.agents-table-wrap {
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.agents-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
.agents-table th {
|
||
text-align: left;
|
||
padding: 0.7rem 1rem;
|
||
color: var(--text-muted);
|
||
font-weight: 500;
|
||
font-size: 0.7rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
background: var(--bg-surface);
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
|
||
.agents-table td {
|
||
padding: 0.6rem 1rem;
|
||
border-bottom: 1px solid var(--border);
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.agents-table tr:last-child td { border-bottom: none; }
|
||
|
||
.agents-table td:first-child {
|
||
color: var(--text);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.agents-table a {
|
||
color: inherit;
|
||
text-decoration: none;
|
||
border-bottom: 1px solid var(--border);
|
||
transition: all 0.2s;
|
||
}
|
||
.agents-table a:hover { color: var(--green); border-color: var(--green); }
|
||
|
||
.check { color: var(--green); }
|
||
.partial { color: var(--yellow); }
|
||
.untested { color: var(--text-dim); font-size: 0.7rem; }
|
||
|
||
/* ── How it works ── */
|
||
.how-section {
|
||
max-width: var(--max-w);
|
||
margin: 0 auto;
|
||
padding: 0 2rem;
|
||
}
|
||
|
||
.how-section h2 {
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
|
||
.how-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 1.5rem;
|
||
}
|
||
|
||
.how-step {
|
||
background: var(--bg-raised);
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 1.5rem;
|
||
}
|
||
|
||
.how-step .step-num {
|
||
font-size: 0.7rem;
|
||
color: var(--green);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.how-step h3 {
|
||
font-size: 0.85rem;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
margin-bottom: 0.4rem;
|
||
}
|
||
|
||
.how-step p {
|
||
font-size: 0.75rem;
|
||
color: var(--text-muted);
|
||
line-height: 1.65;
|
||
}
|
||
|
||
.how-step code {
|
||
font-size: 0.725rem;
|
||
background: var(--bg);
|
||
padding: 0.15rem 0.35rem;
|
||
border-radius: 3px;
|
||
color: var(--text);
|
||
}
|
||
|
||
/* ── API teaser ── */
|
||
.api-section {
|
||
max-width: var(--max-w);
|
||
margin: 0 auto;
|
||
padding: 0 2rem;
|
||
}
|
||
|
||
.api-card {
|
||
background: var(--bg-raised);
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 2rem;
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 2rem;
|
||
align-items: start;
|
||
}
|
||
|
||
.api-card h2 {
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
margin-bottom: 0.3rem;
|
||
}
|
||
|
||
.api-card .subtitle {
|
||
font-size: 0.75rem;
|
||
color: var(--green);
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.api-card p {
|
||
font-size: 0.8rem;
|
||
color: var(--text-muted);
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.api-card p + p { margin-top: 0.6rem; }
|
||
|
||
.api-doc-links {
|
||
display: flex;
|
||
gap: 0.75rem;
|
||
margin-top: 1.25rem;
|
||
}
|
||
|
||
.api-doc-links a {
|
||
font-size: 0.75rem;
|
||
color: var(--text);
|
||
text-decoration: none;
|
||
padding: 0.4rem 0.75rem;
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
transition: all 0.2s;
|
||
}
|
||
.api-doc-links a:hover { border-color: var(--green); color: var(--green); }
|
||
|
||
.code-block {
|
||
background: var(--bg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
padding: 1.25rem;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.code-block pre {
|
||
font-size: 0.725rem;
|
||
line-height: 1.8;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.code-block .comment { color: var(--text-dim); }
|
||
.code-block .cmd { color: var(--green); }
|
||
.code-block .flag { color: var(--blue); }
|
||
.code-block .str { color: var(--yellow); }
|
||
|
||
/* ── Footer ── */
|
||
.footer {
|
||
max-width: var(--max-w);
|
||
margin: 0 auto;
|
||
padding: 4rem 2rem 3rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.footer-line {
|
||
border: none;
|
||
border-top: 1px solid var(--border);
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.footer p {
|
||
font-size: 0.75rem;
|
||
color: var(--text-dim);
|
||
line-height: 1.8;
|
||
}
|
||
|
||
.footer a {
|
||
color: var(--text-muted);
|
||
text-decoration: none;
|
||
transition: color 0.2s;
|
||
}
|
||
.footer a:hover { color: var(--green); }
|
||
|
||
/* ── Responsive ── */
|
||
@media (max-width: 768px) {
|
||
.hero { padding-top: 3rem; }
|
||
.hero h1 { font-size: 2.25rem; }
|
||
.features { grid-template-columns: 1fr; }
|
||
.how-grid { grid-template-columns: 1fr; }
|
||
.api-card { grid-template-columns: 1fr; }
|
||
.awareness-dual { grid-template-columns: 1fr; }
|
||
.state-bar { gap: 1rem; flex-wrap: wrap; }
|
||
.hero-screenshot { padding: 0 1rem; }
|
||
.state-grid { grid-template-columns: 1fr 1fr; }
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.hero h1 { font-size: 1.75rem; }
|
||
.install-box { font-size: 0.7rem; padding: 0.5rem 0.75rem; }
|
||
.state-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ── Hero ── -->
|
||
<section class="hero" id="hero">
|
||
<img src="/assets/logo.png" alt="herdr" class="hero-logo">
|
||
<h1>herdr</h1>
|
||
<p class="hero-tagline">herd your agents.</p>
|
||
<div class="hero-desc">
|
||
<p>a terminal-native agent multiplexer. workspaces, tiled panes, automatic agent detection, and a socket API. all inside your existing terminal. no electron, no web dashboard, no gui window.</p>
|
||
<p>a shared control surface for you and your agents.</p>
|
||
</div>
|
||
<div class="install-box">
|
||
<span class="prompt">$</span>
|
||
<code id="install-cmd">curl -fsSL https://herdr.dev/install.sh | sh</code>
|
||
<button class="copy-btn" id="copy-btn">copy</button>
|
||
</div>
|
||
<div class="hero-links">
|
||
<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>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ── Demo ── -->
|
||
<div class="hero-screenshot">
|
||
<div class="screenshot-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="section-gap"></div>
|
||
|
||
<!-- ── For you / For your agents ── -->
|
||
<section class="features">
|
||
<div class="feature-card">
|
||
<h2>for you</h2>
|
||
<p class="subtitle">supervise, triage, act</p>
|
||
<ul class="feature-list">
|
||
<li>one place to see all running agents across workspaces</li>
|
||
<li>sidebar rolls up each workspace to its most urgent state</li>
|
||
<li>switch contexts instantly — click or keyboard</li>
|
||
<li>sound alerts and toast notifications for background events</li>
|
||
<li>mouse-native: click panes, drag borders, select text to copy</li>
|
||
<li>session persistence — close and reopen, everything restores</li>
|
||
<li>9 built-in themes, full color token overrides</li>
|
||
</ul>
|
||
</div>
|
||
<div class="feature-card">
|
||
<h2>for your agents</h2>
|
||
<p class="subtitle">orchestrate, spawn, wait</p>
|
||
<ul class="feature-list">
|
||
<li>create workspaces and split panes programmatically</li>
|
||
<li>spawn other agents in sibling panes</li>
|
||
<li>read output from any pane in the session</li>
|
||
<li>send text and keys into other panes</li>
|
||
<li>wait for output patterns or agent state changes</li>
|
||
<li>subscribe to real-time events over the socket</li>
|
||
<li>ship an agent skill or use the socket API directly</li>
|
||
</ul>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="section-gap"></div>
|
||
|
||
<!-- ── Awareness ── -->
|
||
<section class="awareness">
|
||
<div class="awareness-header">
|
||
<h2>ambient awareness</h2>
|
||
<p class="awareness-desc">the sidebar is split into two layers. scan the workspace list for the most urgent signal, then drill into the agents causing it.</p>
|
||
</div>
|
||
<div class="awareness-dual">
|
||
<div class="awareness-panel">
|
||
<div class="awareness-panel-img">
|
||
<img src="/assets/sidebar-workspaces.png" alt="herdr sidebar workspace list">
|
||
</div>
|
||
<div class="awareness-panel-text">
|
||
<h3>workspaces</h3>
|
||
<p>each workspace shows one aggregate dot — the most urgent state across all its agents. yellow means something is working. you 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="herdr sidebar agent list">
|
||
</div>
|
||
<div class="awareness-panel-text">
|
||
<h3>agents</h3>
|
||
<p>the bottom section shows which specific agents are running in the selected workspace, with their individual state. pi is working, droid is idle.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="state-bar">
|
||
<div class="state-bar-item"><span class="state-dot red"></span> blocked</div>
|
||
<div class="state-bar-item"><span class="state-dot blue"></span> done</div>
|
||
<div class="state-bar-item"><span class="state-dot yellow"></span> working</div>
|
||
<div class="state-bar-item"><span class="state-dot green"></span> idle</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="section-gap"></div>
|
||
|
||
<!-- ── How it works ── -->
|
||
<section class="how-section">
|
||
<h2>how it works</h2>
|
||
<div class="how-grid">
|
||
<div class="how-step">
|
||
<div class="step-num">01</div>
|
||
<h3>install and launch</h3>
|
||
<p>one binary, no dependencies. run <code>herdr</code> in your terminal. onboarding takes 10 seconds.</p>
|
||
</div>
|
||
<div class="how-step">
|
||
<div class="step-num">02</div>
|
||
<h3>create workspaces</h3>
|
||
<p>press <code>n</code> to create a workspace. it opens as a terminal context, labels itself from git repo or folder name.</p>
|
||
</div>
|
||
<div class="how-step">
|
||
<div class="step-num">03</div>
|
||
<h3>launch agents</h3>
|
||
<p>start your agents in panes like you normally would. herdr detects them automatically — no hooks, no config.</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="section-gap"></div>
|
||
|
||
<!-- ── Supported agents ── -->
|
||
<section class="agents-section">
|
||
<h2>supported agents</h2>
|
||
<p class="section-desc">herdr detects agents by reading foreground process and terminal output patterns. zero config required.</p>
|
||
<div class="agents-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="partial">partial</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="section-gap"></div>
|
||
|
||
<!-- ── API ── -->
|
||
<section class="api-section">
|
||
<div class="api-card">
|
||
<div>
|
||
<h2>socket api</h2>
|
||
<p class="subtitle">agents can use herdr too</p>
|
||
<p>herdr exposes a local unix socket that agents can talk to directly. create workspaces, split panes, spawn agents, read output, wait for state changes.</p>
|
||
<p>the same surface is available as CLI commands (<code>herdr workspace</code>, <code>herdr pane</code>, <code>herdr wait</code>) and as a reusable agent skill.</p>
|
||
<div class="api-doc-links">
|
||
<a href="https://github.com/ogulcancelik/herdr/blob/master/SOCKET_API.md">socket api docs →</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</span>
|
||
<span class="cmd">herdr</span> workspace create <span class="flag">--root</span> <span class="str">~/project</span>
|
||
|
||
<span class="comment"># split a pane and run a command</span>
|
||
<span class="cmd">herdr</span> pane split <span class="flag">--workspace</span> <span class="str">1</span>
|
||
<span class="cmd">herdr</span> pane send <span class="flag">--pane</span> <span class="str">1-2</span> <span class="flag">--text</span> <span class="str">"npm test"</span>
|
||
|
||
<span class="comment"># wait for an agent to finish</span>
|
||
<span class="cmd">herdr</span> wait <span class="flag">--pane</span> <span class="str">1-1</span> <span class="flag">--state</span> <span class="str">idle</span>
|
||
|
||
<span class="comment"># read pane output</span>
|
||
<span class="cmd">herdr</span> pane read <span class="flag">--pane</span> <span class="str">1-2</span> <span class="flag">--lines</span> <span class="str">50</span></pre>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="section-gap"></div>
|
||
|
||
<!-- ── Footer ── -->
|
||
<footer class="footer">
|
||
<hr class="footer-line">
|
||
<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>
|
||
<p style="margin-top: 0.75rem;">linux · macos · agpl-3.0</p>
|
||
</footer>
|
||
|
||
<script>
|
||
(function() {
|
||
'use strict';
|
||
|
||
// ── Sheep ──
|
||
var hero = document.getElementById('hero');
|
||
var pasture = document.createElement('div');
|
||
pasture.className = 'hero-pasture';
|
||
pasture.setAttribute('aria-hidden', 'true');
|
||
hero.prepend(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 ? 4 : 8;
|
||
|
||
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 = 10 + Math.random() * 4;
|
||
el.style.cssText = 'font-size:' + size + 'px;color:rgba(255,255,255,' + 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.15 + Math.random() * 0.3,
|
||
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; }
|
||
|
||
// mouse scatter
|
||
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.6;
|
||
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>
|