<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sparrow — Route, run, replay, rewind.</title>
<meta name="description" content="Sparrow is a local-first Rust agent cockpit. One binary. Git-backed checkpoints. Budget-aware routing. Built for builders who refuse to be locked in." />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root{
--bg: #0e0b08;
--bg-elev: #16120d;
--bg-soft: #1d1710;
--line: #2c251c;
--line-soft: #221c14;
--dim: #897d6c;
--dimmer: #5c5346;
--fg: #ece2cf;
--fg-soft: #c8bea9;
--amber: #f2a93c;
--coral: #f0674a;
--cream: #f7d089;
--gold: #f2c94c;
--ink: #221910;
--agent: #4ec9b0;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{background:var(--bg);color:var(--fg);font-family:'Inter',system-ui,sans-serif;
-webkit-font-smoothing:antialiased;line-height:1.5;overflow-x:hidden}
a{color:inherit;text-decoration:none}
code,pre,.mono{font-family:'JetBrains Mono',ui-monospace,monospace}
body{
background:
radial-gradient(1100px 700px at 70% -10%, rgba(242,169,60,.14), transparent 60%),
radial-gradient(900px 600px at 12% 112%, rgba(240,103,74,.10), transparent 55%),
radial-gradient(700px 500px at 50% 50%, rgba(78,201,176,.04), transparent 60%),
var(--bg);
}
body::after{
content:"";position:fixed;inset:0;pointer-events:none;opacity:.05;z-index:1;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")
}
.wrap{max-width:1180px;margin:0 auto;padding:0 28px;position:relative;z-index:2}
nav{
display:flex;align-items:center;justify-content:space-between;
padding:24px 28px;max-width:1180px;margin:0 auto;position:relative;z-index:5;
}
.nav-brand{display:flex;align-items:center;gap:12px;font-weight:700;letter-spacing:3px;font-size:14px}
.nav-brand .dot{width:10px;height:10px;border-radius:50%;background:var(--amber);
box-shadow:0 0 14px var(--amber);animation:pulse 2.4s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(.85)}}
.nav-links{display:flex;gap:32px;font-size:13px;color:var(--fg-soft)}
.nav-links a:hover{color:var(--amber)}
.nav-cta{padding:9px 16px;border:1px solid var(--line);border-radius:8px;font-size:12.5px;
color:var(--fg);transition:.2s;background:linear-gradient(180deg,#1d1710,#15110b)}
.nav-cta:hover{border-color:var(--amber);box-shadow:0 0 0 3px rgba(242,169,60,.12)}
.hero{padding:80px 0 100px;text-align:center;position:relative}
.eyebrow{display:inline-flex;align-items:center;gap:8px;padding:6px 14px;border:1px solid var(--line);
border-radius:99px;font-size:11px;letter-spacing:2px;text-transform:uppercase;color:var(--dim);
background:rgba(29,23,16,.5);backdrop-filter:blur(6px);margin-bottom:32px}
.eyebrow .tag{color:var(--amber);font-weight:600}
h1.manifesto{
font-family:'Space Grotesk',sans-serif;font-weight:700;letter-spacing:-2px;line-height:.98;
font-size:clamp(44px,8vw,108px);margin-bottom:28px;
}
h1.manifesto .grad{
background:linear-gradient(90deg,var(--amber) 0%,var(--coral) 60%,var(--gold) 100%);
-webkit-background-clip:text;background-clip:text;color:transparent;
display:inline-block;
}
h1.manifesto .strike{position:relative;display:inline-block;color:var(--dimmer)}
h1.manifesto .strike::after{
content:"";position:absolute;left:-4%;right:-4%;top:52%;height:6px;
background:linear-gradient(90deg,var(--coral),var(--amber));transform:rotate(-3deg);
border-radius:3px;
}
.lede{font-size:clamp(16px,1.6vw,20px);color:var(--fg-soft);max-width:680px;margin:0 auto 44px;
line-height:1.55}
.lede b{color:var(--fg);font-weight:600}
.lede .accent{color:var(--amber);font-weight:600}
.cta-row{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-bottom:80px}
.btn{padding:14px 22px;border-radius:10px;font-size:14px;font-weight:600;letter-spacing:.3px;
display:inline-flex;align-items:center;gap:10px;transition:.2s;cursor:pointer;border:0}
.btn-primary{
background:linear-gradient(135deg,var(--amber),var(--coral));color:var(--ink);
box-shadow:0 10px 40px -10px rgba(242,169,60,.45);
}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 14px 50px -10px rgba(242,169,60,.65)}
.btn-ghost{background:transparent;border:1px solid var(--line);color:var(--fg)}
.btn-ghost:hover{border-color:var(--amber);color:var(--amber)}
.terminal{
max-width:920px;margin:0 auto;border:1px solid var(--line);border-radius:14px;
background:linear-gradient(180deg,#15110b,#100d09);
box-shadow:0 60px 120px -40px rgba(0,0,0,.8),inset 0 1px 0 rgba(255,255,255,.04);
overflow:hidden;
}
.term-bar{display:flex;align-items:center;gap:8px;padding:14px 18px;border-bottom:1px solid var(--line);
background:#0a0805}
.term-bar .d{width:11px;height:11px;border-radius:50%}
.term-bar .d1{background:#f0674a}.term-bar .d2{background:#f2c94c}.term-bar .d3{background:#4ec9b0}
.term-bar .path{margin-left:auto;font-size:11.5px;color:var(--dim);font-family:'JetBrains Mono',monospace}
.term-body{padding:24px 26px 28px;font-size:13.5px;line-height:1.7;min-height:340px}
.term-prompt{color:var(--agent)}
.term-cmd{color:var(--fg)}
.term-out{color:var(--fg-soft)}
.term-dim{color:var(--dim)}
.term-amber{color:var(--amber)}
.term-coral{color:var(--coral)}
.term-cream{color:var(--cream)}
.term-gold{color:var(--gold)}
.term-route{display:inline-flex;gap:6px;align-items:center;padding:2px 8px;border-radius:5px;
background:rgba(78,201,176,.12);color:var(--agent);font-size:11.5px;margin:0 4px}
.term-route.local{background:rgba(78,201,176,.15);color:var(--agent)}
.term-route.cloud{background:rgba(242,201,76,.12);color:var(--gold)}
.cursor{display:inline-block;width:8px;height:15px;background:var(--amber);vertical-align:middle;
animation:blink 1.05s step-end infinite;margin-left:2px}
@keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
section{padding:110px 0;position:relative}
.sec-label{display:inline-block;font-size:11px;letter-spacing:3px;text-transform:uppercase;
color:var(--amber);font-weight:600;margin-bottom:18px}
h2.section{font-family:'Space Grotesk',sans-serif;font-weight:700;letter-spacing:-1.2px;
font-size:clamp(34px,4.6vw,56px);line-height:1.05;margin-bottom:24px;max-width:880px}
h2.section .grad{background:linear-gradient(90deg,var(--amber),var(--coral));
-webkit-background-clip:text;background-clip:text;color:transparent}
p.lead{color:var(--fg-soft);font-size:17px;max-width:680px;line-height:1.6;margin-bottom:64px}
.pillars{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;margin-top:8px}
@media(max-width:980px){.pillars{grid-template-columns:repeat(2,1fr)}}
@media(max-width:560px){.pillars{grid-template-columns:1fr}}
.pillar{border:1px solid var(--line);border-radius:14px;padding:28px 24px;background:linear-gradient(180deg,#15110b,#100d09);
position:relative;overflow:hidden;transition:.25s}
.pillar::before{content:"";position:absolute;inset:0;background:radial-gradient(400px 200px at 50% 0%,rgba(242,169,60,.08),transparent 60%);opacity:0;transition:.25s}
.pillar:hover{transform:translateY(-3px);border-color:rgba(242,169,60,.4)}
.pillar:hover::before{opacity:1}
.pillar .n{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--dim);letter-spacing:1px}
.pillar .ico{width:42px;height:42px;border-radius:10px;background:rgba(242,169,60,.1);
display:flex;align-items:center;justify-content:center;margin:14px 0 18px;color:var(--amber)}
.pillar h3{font-size:19px;font-weight:700;margin-bottom:10px;letter-spacing:-.3px}
.pillar p{font-size:14px;color:var(--fg-soft);line-height:1.55}
.pillar .verdict{margin-top:16px;padding-top:14px;border-top:1px dashed var(--line);
font-size:12.5px;color:var(--dim)}
.pillar .verdict b{color:var(--coral)}
.story{display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:center}
@media(max-width:900px){.story{grid-template-columns:1fr;gap:40px}}
.story-list{display:flex;flex-direction:column;gap:22px}
.story-row{display:flex;gap:18px;align-items:flex-start}
.story-row .mark{flex-shrink:0;width:30px;height:30px;border-radius:8px;display:flex;align-items:center;
justify-content:center;font-weight:700;font-size:14px;font-family:'JetBrains Mono',monospace}
.story-row.bad .mark{background:rgba(240,103,74,.12);color:var(--coral);border:1px solid rgba(240,103,74,.3)}
.story-row.good .mark{background:rgba(78,201,176,.12);color:var(--agent);border:1px solid rgba(78,201,176,.3)}
.story-row h4{font-size:15.5px;font-weight:600;margin-bottom:4px}
.story-row p{font-size:14px;color:var(--fg-soft);line-height:1.55}
.story-row .mono{font-size:12.5px;color:var(--dim);display:block;margin-top:6px}
.brain-card{border:1px solid var(--line);border-radius:18px;background:linear-gradient(180deg,#15110b,#100d09);
padding:32px;position:relative;overflow:hidden}
.brain-card::before{content:"";position:absolute;width:280px;height:280px;border-radius:50%;
background:radial-gradient(circle,rgba(242,169,60,.18),transparent 70%);top:-100px;right:-100px}
.brain-card h5{font-size:11px;letter-spacing:2px;text-transform:uppercase;color:var(--dim);
font-weight:600;margin-bottom:18px;font-family:'JetBrains Mono',monospace}
.chain{display:flex;flex-direction:column;gap:14px;position:relative;z-index:2}
.hop{display:flex;align-items:center;gap:14px;padding:14px;border:1px solid var(--line);border-radius:10px;
background:rgba(14,11,8,.6);font-size:13.5px;transition:.2s}
.hop.hit{opacity:1}
.hop.skip{opacity:.4;text-decoration:line-through;text-decoration-color:var(--coral)}
.hop .tag{font-family:'JetBrains Mono',monospace;font-size:10px;letter-spacing:1px;
padding:3px 8px;border-radius:5px}
.hop .tag.local{background:rgba(78,201,176,.12);color:var(--agent)}
.hop .tag.cloud{background:rgba(242,201,76,.12);color:var(--gold)}
.hop .cost{margin-left:auto;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--dim)}
.hop.hit .cost{color:var(--amber);font-weight:600}
.hop .name{flex:1}
.hop .name b{color:var(--fg);font-weight:600}
.hop .name span{color:var(--dim);font-size:12px;display:block}
.feat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
@media(max-width:900px){.feat-grid{grid-template-columns:1fr 1fr}}
@media(max-width:580px){.feat-grid{grid-template-columns:1fr}}
.feat{border:1px solid var(--line);border-radius:14px;padding:26px;background:var(--bg-elev);
transition:.2s;position:relative;overflow:hidden}
.feat:hover{border-color:rgba(242,169,60,.35);transform:translateY(-2px)}
.feat .num{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--dim);
letter-spacing:1px;margin-bottom:14px}
.feat h4{font-size:17px;font-weight:700;margin-bottom:10px;letter-spacing:-.2px;
display:flex;align-items:center;gap:10px}
.feat h4 .dot{width:8px;height:8px;border-radius:50%;background:var(--amber)}
.feat p{font-size:13.5px;color:var(--fg-soft);line-height:1.55}
.feat code{background:var(--bg-soft);padding:2px 6px;border-radius:4px;font-size:12px;color:var(--cream);
border:1px solid var(--line)}
.docs-hub{display:grid;grid-template-columns:minmax(260px,.72fr) minmax(320px,1.28fr);gap:22px;align-items:start}
@media(max-width:960px){.docs-hub{grid-template-columns:1fr}}
.docs-panel{border:1px solid var(--line);border-radius:14px;background:linear-gradient(180deg,#15110b,#100d09);overflow:hidden}
.docs-panel .head{padding:16px 18px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:10px}
.docs-panel .head b{font-size:13px;letter-spacing:1.4px;text-transform:uppercase;color:var(--amber)}
.docs-panel .body{padding:16px 18px}
.docs-search{display:flex;gap:10px;align-items:center;border:1px solid var(--line);border-radius:10px;background:#0a0805;padding:10px 12px;margin-bottom:12px}
.docs-search input{flex:1;background:transparent;border:0;outline:0;color:var(--fg);font:500 14px 'Inter',system-ui,sans-serif}
.docs-search input::placeholder{color:var(--dim)}
.docs-result{display:block;padding:12px 0;border-top:1px solid var(--line);transition:.15s}
.docs-result:first-of-type{border-top:0}
.docs-result:hover{color:var(--amber)}
.docs-result .r-title{font-weight:700;font-size:14px;margin-bottom:4px}
.docs-result .r-body{font-size:12.5px;color:var(--fg-soft);line-height:1.45}
.docs-result .r-tags{margin-top:6px;font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--dim)}
.example-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:14px}
@media(max-width:720px){.example-grid{grid-template-columns:1fr}}
.example-card,.api-card,.video-card{border:1px solid var(--line);border-radius:12px;background:var(--bg-elev);padding:16px;position:relative;overflow:hidden}
.example-card h4,.api-card h4,.video-card h4{font-size:15px;margin-bottom:8px;letter-spacing:-.2px}
.example-card p,.api-card p,.video-card p{font-size:13px;color:var(--fg-soft);line-height:1.5;margin-bottom:12px}
.example-card pre{background:#0a0805;border:1px solid var(--line);border-radius:8px;padding:12px;overflow:auto;font-size:12px;color:var(--cream)}
.copy-mini{position:absolute;top:12px;right:12px;border:1px solid var(--line);border-radius:7px;background:#0a0805;color:var(--amber);font-size:11px;padding:4px 8px;cursor:pointer}
.api-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:20px}
@media(max-width:920px){.api-grid{grid-template-columns:1fr}}
.api-card ul{display:flex;flex-direction:column;gap:7px;list-style:none;font-size:12.5px;color:var(--fg-soft)}
.api-card code{color:var(--cream);background:#0a0805;border:1px solid var(--line);border-radius:5px;padding:1px 5px}
.video-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:20px}
@media(max-width:920px){.video-grid{grid-template-columns:1fr}}
.video-thumb{height:128px;border-radius:10px;border:1px solid var(--line);background:
radial-gradient(circle at 50% 50%,rgba(242,169,60,.22),transparent 34%),
linear-gradient(135deg,#1d1710,#0a0805);display:flex;align-items:center;justify-content:center;margin-bottom:12px}
.video-thumb .play{width:46px;height:46px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--amber),var(--coral));color:var(--ink);font-weight:900}
.video-card video{width:100%;aspect-ratio:16/9;border-radius:10px;border:1px solid var(--line);background:#0a0805;margin-bottom:12px;display:block}
.video-card a{color:var(--amber);font-size:12px;font-weight:700}
.chapter-list{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--dim);line-height:1.8}
.code-wrap{max-width:920px;margin:0 auto;border:1px solid var(--line);border-radius:14px;
background:linear-gradient(180deg,#15110b,#100d09);overflow:hidden}
.code-bar{display:flex;align-items:center;gap:10px;padding:12px 18px;border-bottom:1px solid var(--line);
background:#0a0805;font-size:12px;color:var(--dim);font-family:'JetBrains Mono',monospace}
.code-bar .file{color:var(--cream)}
.code-body{padding:22px 26px;font-size:13.5px;line-height:1.7;overflow-x:auto}
.code-body .c{color:var(--dim)}
.code-body .k{color:var(--coral)}
.code-body .s{color:var(--cream)}
.code-body .v{color:var(--amber)}
.code-body .p{color:var(--agent)}
.code-body .o{color:var(--gold)}
.timeline{position:relative;padding-left:40px;margin-top:24px}
.timeline::before{content:"";position:absolute;left:14px;top:6px;bottom:6px;width:1px;background:var(--line)}
.tl-item{position:relative;padding:0 0 36px 0}
.tl-item .dot{position:absolute;left:-33px;top:6px;width:14px;height:14px;border-radius:50%;
background:var(--bg);border:2px solid var(--amber);box-shadow:0 0 0 4px rgba(242,169,60,.12)}
.tl-item.done .dot{background:var(--amber);box-shadow:0 0 0 4px rgba(242,169,60,.2)}
.tl-item .when{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--dim);
letter-spacing:1.5px;text-transform:uppercase;margin-bottom:6px}
.tl-item h4{font-size:18px;font-weight:700;margin-bottom:6px}
.tl-item p{font-size:14px;color:var(--fg-soft);line-height:1.6;max-width:680px}
.tl-item .pill{display:inline-block;padding:3px 8px;border-radius:5px;font-size:11px;
font-family:'JetBrains Mono',monospace;margin-top:10px}
.tl-item .pill.shipped{background:rgba(78,201,176,.12);color:var(--agent)}
.tl-item .pill.next{background:rgba(242,169,60,.12);color:var(--amber)}
.tl-item .pill.soon{background:rgba(242,201,76,.12);color:var(--gold)}
.quote{border:1px solid var(--line);border-radius:18px;padding:48px 40px;text-align:center;
background:linear-gradient(180deg,rgba(242,169,60,.05),transparent),var(--bg-elev);
max-width:880px;margin:0 auto;position:relative;overflow:hidden}
.quote::before{content:"";position:absolute;width:400px;height:400px;border-radius:50%;
background:radial-gradient(circle,rgba(242,169,60,.12),transparent 70%);top:-200px;left:50%;
transform:translateX(-50%);pointer-events:none}
.quote blockquote{font-family:'Space Grotesk',sans-serif;font-size:clamp(22px,2.6vw,30px);
line-height:1.35;font-weight:500;letter-spacing:-.4px;position:relative;z-index:2}
.quote blockquote b{background:linear-gradient(90deg,var(--amber),var(--coral));
-webkit-background-clip:text;background-clip:text;color:transparent;font-weight:700}
.quote cite{display:block;margin-top:22px;font-style:normal;font-size:13px;color:var(--dim);
font-family:'JetBrains Mono',monospace;letter-spacing:1px}
.install{max-width:760px;margin:0 auto;border:1px solid var(--line);border-radius:14px;
background:linear-gradient(180deg,#15110b,#100d09);overflow:hidden}
.install-bar{display:flex;align-items:center;justify-content:space-between;padding:12px 18px;
border-bottom:1px solid var(--line);background:#0a0805;font-size:12px;color:var(--dim);
font-family:'JetBrains Mono',monospace}
.install-bar .copy{cursor:pointer;color:var(--amber);font-size:11.5px}
.install-body{padding:24px 26px;font-size:15px;font-family:'JetBrains Mono',monospace}
.install-body .p{color:var(--agent)}
.install-body .c{color:var(--fg)}
.final-cta{text-align:center;padding:120px 0 140px;position:relative}
.final-cta::before{content:"";position:absolute;width:700px;height:700px;border-radius:50%;
background:radial-gradient(circle,rgba(242,169,60,.16),transparent 70%);top:50%;left:50%;
transform:translate(-50%,-50%);pointer-events:none}
.final-cta h2{font-family:'Space Grotesk',sans-serif;font-weight:700;letter-spacing:-1.5px;
font-size:clamp(40px,6vw,84px);line-height:1;margin-bottom:24px;position:relative;z-index:2}
.final-cta h2 .grad{background:linear-gradient(90deg,var(--amber),var(--coral),var(--gold));
-webkit-background-clip:text;background-clip:text;color:transparent}
.final-cta p{color:var(--fg-soft);font-size:18px;max-width:540px;margin:0 auto 36px;
position:relative;z-index:2}
.final-cta .cta-row{position:relative;z-index:2}
footer{padding:50px 0;border-top:1px solid var(--line);position:relative;z-index:2}
.foot{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:18px;
color:var(--dim);font-size:13px}
.foot .links{display:flex;gap:24px}
.foot .links a:hover{color:var(--amber)}
.foot .legal{font-family:'JetBrains Mono',monospace;font-size:12px}
.mascot{width:64px;height:64px;display:inline-block;vertical-align:middle}
.mascot-lg{width:120px;height:120px}
.mascot-svg{filter:drop-shadow(0 8px 18px rgba(0,0,0,.5)) drop-shadow(0 0 24px rgba(242,169,60,.2))}
.eye-open{transform-box:fill-box;transform-origin:center;animation:blink 4.4s infinite}
@keyframes blink{0%,93%,100%{transform:scaleY(1)}96.5%{transform:scaleY(.08)}}
.tool-anim{transform-box:fill-box;transform-origin:bottom center;animation:tap 2.6s ease-in-out infinite}
@keyframes tap{0%,100%{rotate:26deg}50%{rotate:34deg}}
.hero-mascot{display:inline-block;animation:bob 3.6s ease-in-out infinite;margin-bottom:8px;
filter:drop-shadow(0 16px 30px rgba(0,0,0,.5)) drop-shadow(0 0 40px rgba(242,169,60,.25))}
@keyframes bob{0%,100%{transform:translateY(0) rotate(-1.2deg)}50%{transform:translateY(-8px) rotate(1.2deg)}}
::selection{background:rgba(242,169,60,.3);color:var(--fg)}
html{scroll-behavior:smooth}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<defs>
<linearGradient id="body" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#f8bf57"/><stop offset="1" stop-color="#ef9b2f"/>
</linearGradient>
<linearGradient id="wing" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#e8901f"/><stop offset="1" stop-color="#d2761d"/>
</linearGradient>
<linearGradient id="belly" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#fde6bd"/><stop offset="1" stop-color="#f7d089"/>
</linearGradient>
<linearGradient id="steel" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#cdc6ba"/><stop offset="1" stop-color="#8f877a"/>
</linearGradient>
<symbol id="sparrow" viewBox="0 0 240 240">
<path d="M150 150 q58 6 60 52 q-46 -2 -64 -28 z" fill="url(#wing)"/>
<path d="M120 50 C168 50 182 92 182 132 C182 184 154 204 120 204
C86 204 58 184 58 132 C58 92 72 50 120 50 Z"
fill="url(#body)" stroke="#7a4f1c" stroke-width="3.5"/>
<path d="M112 52 q-4 -22 8 -28 q3 12 0 27 z" fill="url(#wing)" stroke="#7a4f1c" stroke-width="2.4"/>
<path d="M122 50 q6 -20 18 -20 q-2 12 -12 24 z" fill="url(#wing)" stroke="#7a4f1c" stroke-width="2.4"/>
<ellipse cx="118" cy="150" rx="40" ry="46" fill="url(#belly)"/>
<path d="M70 110 C58 132 60 168 86 182 C92 156 88 128 84 112 Z"
fill="url(#wing)" stroke="#7a4f1c" stroke-width="3"/>
<path d="M104 124 L130 121 L113 142 Z" fill="#f1862a" stroke="#7a4f1c" stroke-width="2.4" stroke-linejoin="round"/>
<ellipse cx="92" cy="138" rx="9" ry="6" fill="#f0674a" opacity=".45"/>
<g class="eye-open">
<circle cx="100" cy="106" r="17" fill="#fff" stroke="#7a4f1c" stroke-width="2.6"/>
<circle cx="103" cy="108" r="8.5" fill="#2a1c0e"/>
<circle cx="99" cy="103" r="3.4" fill="#fff"/>
</g>
<path d="M86 86 C110 74 150 80 168 100" fill="none" stroke="#221910" stroke-width="6" stroke-linecap="round"/>
<ellipse cx="146" cy="108" rx="20" ry="17" fill="#221910"/>
<ellipse cx="146" cy="106" rx="20" ry="15" fill="#2c2114"/>
<circle cx="150" cy="138" r="7" fill="none" stroke="#f2c94c" stroke-width="3.4"/>
<path d="M138 150 C166 150 178 168 168 192 C150 188 138 172 134 156 Z"
fill="url(#wing)" stroke="#7a4f1c" stroke-width="3"/>
<g transform="translate(156,176)">
<g class="tool-anim" transform="rotate(28)">
<rect x="-5" y="-44" width="10" height="46" rx="5" fill="url(#steel)" stroke="#5f584d" stroke-width="2"/>
<path d="M-11 -44 L11 -44 L11 -58 L4.5 -58 L4.5 -50 L-4.5 -50 L-4.5 -58 L-11 -58 Z"
fill="url(#steel)" stroke="#5f584d" stroke-width="2" stroke-linejoin="round"/>
<rect x="-2" y="-40" width="2.6" height="34" rx="1.3" fill="#fff" opacity=".35"/>
</g>
</g>
<g stroke="#e07e26" stroke-width="5" stroke-linecap="round" fill="none">
<path d="M108 200 L108 214 M101 218 L108 214 L115 218 M108 214 L108 220"/>
<path d="M132 200 L132 214 M125 218 L132 214 L139 218 M132 214 L132 220"/>
</g>
</symbol>
</defs>
</svg>
<nav>
<a href="#top" class="nav-brand">
<span class="dot"></span>
<svg class="mascot"><use href="#sparrow"/></svg>
<span>SPARROW</span>
</a>
<div class="nav-links">
<a href="#why">Why</a>
<a href="#features">Features</a>
<a href="#docs">Docs</a>
<a href="#how">How it works</a>
<a href="#roadmap">Roadmap</a>
<a href="#install">Install</a>
</div>
<a class="nav-cta" href="https://github.com/ucav/Sparrow">★ Star on GitHub</a>
</nav>
<header class="hero wrap" id="top">
<div class="eyebrow">
<span class="tag">v0.3.6</span> · Open Source · MIT · Local-first
</div>
<div class="hero-mascot">
<svg class="mascot mascot-lg mascot-svg"><use href="#sparrow"/></svg>
</div>
<h1 class="manifesto">
Stop renting<br/>
your <span class="strike">agents</span>.<br/>
<span class="grad">Start owning them.</span>
</h1>
<p class="lede">
<b>Sparrow</b> is a single-binary, <span class="accent">Rust-native agent cockpit</span> for builders
who refuse to be locked in. One CLI. Git-backed checkpoints. Budget-aware routing.
<b>Every run is visible. Replayable. Yours.</b>
</p>
<div class="cta-row">
<a class="btn btn-primary" href="#install">⚓ Install Sparrow →</a>
<a class="btn btn-ghost" href="https://github.com/ucav/Sparrow">View on GitHub ↗</a>
</div>
<div class="terminal">
<div class="term-bar">
<span class="d d1"></span><span class="d d2"></span><span class="d d3"></span>
<span class="path">~/projects/sparrow · sparrow run "ship the launch page"</span>
</div>
<div class="term-body">
<div><span class="term-prompt">❯</span> <span class="term-cmd">sparrow run "ship the launch page"</span></div>
<div class="term-out" style="margin-top:10px">
<span class="term-dim">[routing]</span> classifying task → <span class="term-amber">build/web · low cost</span>
<br/>
<span class="term-dim">[hop 1]</span> <span class="term-route local">● local</span> <b>qwen2.5-coder:7b</b> · $0.00 · 312ms ✓
<br/>
<span class="term-dim">[plan]</span> 4 steps · est. <span class="term-amber">$0.04</span>
<br/>
<span class="term-dim">[checkpoint]</span> <span class="term-gold">abc123</span> snapshot saved (1.2 MB)
<br/>
<span class="term-dim">[step 1/4]</span> writing <span class="term-cream">index.html</span> <span class="term-amber">ok</span>
<br/>
<span class="term-dim">[step 2/4]</span> inlining assets <span class="term-amber">ok</span>
<br/>
<span class="term-dim">[step 3/4]</span> <span class="term-route cloud">● cloud</span> fallback → <b>claude-sonnet-4.5</b> · $0.02 · for the headline copy
<br/>
<span class="term-dim">[step 4/4]</span> <span class="term-amber">ok</span>
<br/><br/>
<span class="term-dim">total:</span> <span class="term-gold">$0.02</span> · <span class="term-amber">4.1s</span> · <span class="term-route local">● local-first</span>
<br/>
<span class="term-dim">rewind available:</span> <span class="term-cream">sparrow rewind abc123</span>
</div>
<div style="margin-top:8px"><span class="term-prompt">❯</span> <span class="cursor"></span></div>
</div>
</div>
</header>
<section class="wrap" id="why">
<span class="sec-label">// the problem</span>
<h2 class="section">You shipped a prompt. <br/>They shipped <span class="grad">a leash.</span></h2>
<p class="lead">Every agent framework out there is someone else's product strategy in disguise.
Cloud-only. Closed weights. Pricing tiers behind your work. Your repo, their rules.</p>
<div class="story">
<div>
<div class="story-list">
<div class="story-row bad">
<div class="mark">✕</div>
<div>
<h4>Your agent is a SaaS subscription</h4>
<p>API keys, rate limits, surprise invoices, model deprecations. Switch provider, lose your memory.</p>
</div>
</div>
<div class="story-row bad">
<div class="mark">✕</div>
<div>
<h4>Every run is a black box</h4>
<p>You can see the input and the output. The middle? Pray and diff. Roll back? Hope you have a commit.</p>
</div>
</div>
<div class="story-row bad">
<div class="mark">✕</div>
<div>
<h4>"Local" means "bring your own 32GB of VRAM"</h4>
<p>And your own streaming setup. And a doctorate in llama.cpp flags.</p>
</div>
</div>
<div class="story-row bad">
<div class="mark">✕</div>
<div>
<h4>The "agent" dies with the session</h4>
<p>Restart tomorrow, teach it everything again. No memory. No continuity. No identity.</p>
</div>
</div>
</div>
</div>
<div>
<div class="brain-card">
<h5>// how sparrow routes a single request</h5>
<div class="chain">
<div class="hop skip">
<span class="tag cloud">cloud</span>
<div class="name"><b>claude-opus-4.7</b><span>expensive · overkill</span></div>
<div class="cost">~$0.18</div>
</div>
<div class="hop skip">
<span class="tag cloud">cloud</span>
<div class="name"><b>claude-sonnet-4.5</b><span>good but paid</span></div>
<div class="cost">~$0.04</div>
</div>
<div class="hop hit">
<span class="tag local">local</span>
<div class="name"><b>qwen2.5-coder:7b</b><span>handles it cleanly</span></div>
<div class="cost">$0.00</div>
</div>
<div class="hop hit" style="opacity:.7">
<span class="tag local">local</span>
<div class="name"><b>ollama auto-fallback</b><span>standby</span></div>
<div class="cost">$0.00</div>
</div>
</div>
<div style="margin-top:18px;padding-top:16px;border-top:1px dashed var(--line);
font-size:12.5px;color:var(--dim);font-family:'JetBrains Mono',monospace">
→ resolved in 312ms · spent $0.00
</div>
</div>
</div>
</div>
</section>
<section class="wrap">
<span class="sec-label">// the four pillars</span>
<h2 class="section">An agent that respects <span class="grad">the four things you actually own.</span></h2>
<p class="lead">Your code. Your data. Your tokens. Your time. Sparrow is built around all four.</p>
<div class="pillars">
<div class="pillar">
<div class="n">01 / code</div>
<div class="ico">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>
</svg>
</div>
<h3>Git is the memory</h3>
<p>Every mutating action snapshots a checkpoint. Roll back the workspace with one command.
No mystery state. No "what just happened".</p>
<div class="verdict"><b>sparrow rewind <id></b> · restores the exact tree</div>
</div>
<div class="pillar">
<div class="n">02 / data</div>
<div class="ico">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<ellipse cx="12" cy="5" rx="9" ry="3"/>
<path d="M3 5v14a9 3 0 0 0 18 0V5"/><path d="M3 12a9 3 0 0 0 18 0"/>
</svg>
</div>
<h3>Local-first, period</h3>
<p>SQLite facts. Local knowledge graph. SOUL files. Your context lives in <code>./.sparrow/</code>.
Cloud is an explicit fallback, never a default.</p>
<div class="verdict">o llama first · ☁ cloud by choice</div>
</div>
<div class="pillar">
<div class="n">03 / tokens</div>
<div class="ico">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>
</svg>
</div>
<h3>Budget-aware routing</h3>
<p>Each task is classified and routed to the cheapest capable model. Same answer, fractions of the cost.
Set a per-run cap. Watch it in real time.</p>
<div class="verdict">avg run: <b>$0.02</b> · vs ~$0.18 industry</div>
</div>
<div class="pillar">
<div class="n">04 / time</div>
<div class="ico">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<polygon points="5 3 19 12 5 21 5 3"/>
</svg>
</div>
<h3>Every run, replayable</h3>
<p>Full transcript. Tool calls. Token counts. The exact model that answered. Replay any past run,
tweak the input, diff the output.</p>
<div class="verdict"><b>sparrow replay</b> <run-id></div>
</div>
</div>
</section>
<section class="wrap" id="features">
<span class="sec-label">// what's in the box</span>
<h2 class="section">A full cockpit. <span class="grad">Not a chat box.</span></h2>
<p class="lead">Sparrow ships with the surfaces real agents need — terminal, web, gateway — not just a CLI
that pretends an LLM is a coworker.</p>
<div class="feat-grid">
<div class="feat">
<div class="num">// cli</div>
<h4><span class="dot"></span>Terminal-native</h4>
<p>Animated TUI cockpit. <code>sparrow run</code>, <code>sparrow chat</code>, <code>--json</code> for CI,
NDJSON streams for hooks.</p>
</div>
<div class="feat">
<div class="num">// web</div>
<h4><span class="dot"></span>WebView cockpit</h4>
<p>Live route / token / cost / context at <code>http://127.0.0.1:9339/</code>. Drawer panels,
slash palette, agent picker.</p>
</div>
<div class="feat">
<div class="num">// safety</div>
<h4><span class="dot"></span>Sandbox backends</h4>
<p>Linux <code>bwrap</code> wrapper, Docker, SSH, or Git worktree. Pick the blast radius.
Permissions, not promises.</p>
</div>
<div class="feat">
<div class="num">// memory</div>
<h4><span class="dot"></span>Persistent context</h4>
<p>SQLite facts + knowledge graph. <code>.agent.md</code> SOUL files. Guarded skill registry.
Full transcripts retained.</p>
</div>
<div class="feat">
<div class="num">// browser</div>
<h4><span class="dot"></span>Playwright + computer-use</h4>
<p>Browser tool baked in. Gated screenshot, click, type. The web is the filesystem, when you need it.</p>
</div>
<div class="feat">
<div class="num">// gateway</div>
<h4><span class="dot"></span>Multi-channel gateway</h4>
<p>Telegram, Discord, Slack, WebSocket API. Honest errors, not silent failures. Your agent, where you are.</p>
</div>
</div>
</section>
<section class="wrap" id="docs">
<span class="sec-label">// docs that answer back</span>
<h2 class="section">Searchable docs. <span class="grad">Runnable examples.</span></h2>
<p class="lead">Sparrowdev.com is the public documentation surface: quick answers, CLI examples,
tool API references, and short video tutorials in one static page.</p>
<div class="docs-hub">
<aside class="docs-panel" aria-label="Search Sparrow documentation">
<div class="head">
<span class="dot" style="width:8px;height:8px;border-radius:50%;background:var(--agent);box-shadow:0 0 10px var(--agent)"></span>
<b>Search</b>
</div>
<div class="body">
<label class="docs-search">
<span style="color:var(--dim)">⌕</span>
<input id="docsSearch" type="search" placeholder="routing, memory graph, browser, permissions..." autocomplete="off" />
</label>
<div id="docsResults" data-testid="docs-search-results"></div>
</div>
</aside>
<div>
<div class="example-grid" id="examples">
<article class="example-card" data-doc-title="First run" data-doc-tags="setup run provider routing example">
<button class="copy-mini" data-copy="sparrow setup sparrow run "explain this repository and list the risky files"">copy</button>
<h4>First run</h4>
<p>Configure providers, then run a routed task with full transcript and cost tracking.</p>
<pre>sparrow setup
sparrow run "explain this repository and list the risky files"</pre>
</article>
<article class="example-card" data-doc-title="Rollback after edits" data-doc-tags="checkpoint rewind git safety example">
<button class="copy-mini" data-copy="sparrow checkpoint list sparrow rewind abc123">copy</button>
<h4>Rollback after edits</h4>
<p>Every mutating batch creates a checkpoint, so recovery is a command instead of a panic.</p>
<pre>sparrow checkpoint list
sparrow rewind abc123</pre>
</article>
<article class="example-card" data-doc-title="Knowledge graph" data-doc-tags="memory graph neo4j persistent sessions example">
<button class="copy-mini" data-copy="sparrow memory graph upsert-node project:sparrow Sparrow --kind project sparrow memory graph search sparrow">copy</button>
<h4>Knowledge graph</h4>
<p>Persist entities and relationships locally, then optionally sync them to Neo4j.</p>
<pre>sparrow memory graph upsert-node project:sparrow Sparrow --kind project
sparrow memory graph search sparrow</pre>
</article>
<article class="example-card" data-doc-title="Browser/computer-use" data-doc-tags="playwright computer screenshot click type browser example">
<button class="copy-mini" data-copy="npm install npm run browser:install sparrow run "open the local webview and take a screenshot"">copy</button>
<h4>Browser/computer-use</h4>
<p>Install the Playwright runtime and let gated tools screenshot, click, type, and inspect.</p>
<pre>npm install
npm run browser:install
sparrow run "open the local webview and take a screenshot"</pre>
</article>
</div>
<div class="api-grid" id="api">
<article class="api-card" data-doc-title="CLI API" data-doc-tags="api reference cli commands run plan memory graph">
<h4>CLI API</h4>
<p>The commands agents and humans call most often.</p>
<ul>
<li><code>sparrow run <task></code> route and execute a task</li>
<li><code>sparrow plan <task></code> produce a read-only plan</li>
<li><code>sparrow memory graph ...</code> manage graph nodes/edges</li>
<li><code>sparrow console --port 9339</code> open the WebView cockpit</li>
</ul>
</article>
<article class="api-card" data-doc-title="Tool API" data-doc-tags="api reference tools browser computer knowledge_graph exec fs edit">
<h4>Tool API</h4>
<p>Core tool names and capabilities exposed to the engine.</p>
<ul>
<li><code>browser</code> navigate, screenshot, extract, click, type, evaluate</li>
<li><code>computer</code> screenshot, click, type, press with Exec gating</li>
<li><code>knowledge_graph</code> upsert/search/neighbors/export/sync_neo4j</li>
<li><code>exec</code>, <code>fs_read</code>, <code>edit</code>, <code>git</code></li>
</ul>
</article>
<article class="api-card" data-doc-title="WebView API" data-doc-tags="api reference webview endpoints slash commands websocket">
<h4>WebView API</h4>
<p>Local HTTP endpoints used by the cockpit and slash palette.</p>
<ul>
<li><code>GET /commands</code> slash commands with descriptions and usage</li>
<li><code>POST /run</code> submit a task to the active engine</li>
<li><code>POST /cli</code> run non-interactive Sparrow commands</li>
<li><code>GET /memory</code>, <code>/tools</code>, <code>/models</code>, <code>/status</code></li>
</ul>
</article>
</div>
<div class="video-grid" id="videos">
<article class="video-card" data-doc-title="Video tutorial: first launch" data-doc-tags="video tutorial setup providers onboarding">
<video controls preload="metadata" poster="screenshots/webview-captain.png">
<source src="tutorials/first-launch.mp4" type="video/mp4">
</video>
<h4>First launch in 5 minutes</h4>
<p>Install, run setup, add providers, and start the WebView cockpit.</p>
<div class="chapter-list">00:00 install · 00:06 setup · 00:12 cockpit · 00:18 first run</div>
<a href="tutorials/README.md#first-launch-in-5-minutes">Read transcript</a>
</article>
<article class="video-card" data-doc-title="Video tutorial: safe agent edits" data-doc-tags="video tutorial checkpoint rewind permissions sandbox">
<video controls preload="metadata" poster="screenshots/webview-captain.png">
<source src="tutorials/safe-edits-rewind.mp4" type="video/mp4">
</video>
<h4>Safe edits and rewind</h4>
<p>Permissions, checkpoints, diffs, and restoring the workspace after a bad batch.</p>
<div class="chapter-list">00:00 autonomy · 00:06 checkpoint · 00:12 diff · 00:18 rewind</div>
<a href="tutorials/README.md#safe-edits-and-rewind">Read transcript</a>
</article>
<article class="video-card" data-doc-title="Video tutorial: memory graph and browser" data-doc-tags="video tutorial memory graph neo4j browser computer playwright">
<video controls preload="metadata" poster="screenshots/webview-captain.png">
<source src="tutorials/memory-graph-browser.mp4" type="video/mp4">
</video>
<h4>Memory graph + browser-use</h4>
<p>Create graph entities, inspect neighbors, then use Playwright-backed browser actions.</p>
<div class="chapter-list">00:00 graph · 00:06 neighbors · 00:12 Neo4j · 00:18 click/type</div>
<a href="tutorials/README.md#memory-graph--browser-use">Read transcript</a>
</article>
</div>
</div>
</div>
</section>
<section class="wrap" id="how">
<span class="sec-label">// the loop</span>
<h2 class="section">Engine, not magic. <span class="grad">Auditable, not mystical.</span></h2>
<p class="lead">A sparrow doesn't guess. It routes, plans, executes, observes, and emits. You can read the whole loop.</p>
<div class="code-wrap">
<div class="code-bar">
<span class="d d1"></span><span class="d d2"></span><span class="d d3"></span>
<span class="file">.sparrow/runs/2026-06-04-001.toml</span>
</div>
<div class="code-body">
<pre><span class="c"># every run is a file you can grep, diff, and replay</span>
<span class="k">[run]</span>
<span class="v">id</span> = <span class="s">"spar_01HX9P3K..."</span>
<span class="v">task</span> = <span class="s">"ship the launch page"</span>
<span class="v">started</span> = <span class="s">"2026-06-04T17:44:51Z"</span>
<span class="v">budget_usd</span> = <span class="o">0.50</span>
<span class="v">checkpoint</span> = <span class="s">"abc123"</span>
<span class="k">[routing]</span>
<span class="v">classifier</span> = <span class="s">"build/web"</span>
<span class="v">chain</span> = [<span class="s">"qwen2.5-coder:7b"</span>, <span class="s">"claude-sonnet-4.5"</span>]
<span class="v">resolved_on</span> = <span class="s">"qwen2.5-coder:7b"</span>
<span class="v">cost_usd</span> = <span class="o">0.00</span>
<span class="v">latency_ms</span> = <span class="o">312</span>
<span class="k">[[steps]]</span>
<span class="v">i</span> = <span class="o">1</span>
<span class="v">tool</span> = <span class="s">"fs.write"</span>
<span class="v">path</span> = <span class="s">"site/index.html"</span>
<span class="v">bytes</span> = <span class="o">18432</span>
<span class="v">status</span> = <span class="p">"ok"</span>
<span class="k">[[steps]]</span>
<span class="v">i</span> = <span class="o">3</span>
<span class="v">tool</span> = <span class="s">"llm.fallback"</span>
<span class="v">reason</span> = <span class="s">"headline copy needs a stronger model"</span>
<span class="v">resolved_on</span> = <span class="s">"claude-sonnet-4.5"</span>
<span class="v">cost_usd</span> = <span class="o">0.02</span>
<span class="k">[summary]</span>
<span class="v">total_usd</span> = <span class="o">0.02</span>
<span class="v">total_ms</span> = <span class="o">4102</span>
<span class="v">local_share</span> = <span class="o">0.94</span> <span class="c"># 94% of tokens stayed on your machine</span>
</pre>
</div>
</div>
<div style="margin-top:30px;text-align:center">
<p style="color:var(--dim);font-size:13.5px;font-family:'JetBrains Mono',monospace">
→ .sparrow/ is just files. Grep them. Commit them. Back them up.
</p>
</div>
</section>
<section class="wrap" id="roadmap">
<span class="sec-label">// the road</span>
<h2 class="section">Where we've been. <span class="grad">Where we're going.</span></h2>
<p class="lead">Sparrow v0.3.6 is shipping today. Here's the path that got us here — and the next 90 days.</p>
<div class="timeline">
<div class="tl-item done">
<div class="dot"></div>
<div class="when">v0.1 — jan 2026</div>
<h4>The shell loop</h4>
<p>Rust binary. Ollama routing. <code>sparrow run</code>. The barest proof that local-first was possible.</p>
<span class="pill shipped">shipped</span>
</div>
<div class="tl-item done">
<div class="dot"></div>
<div class="when">v0.2 — mar 2026</div>
<h4>Checkpoints & rewind</h4>
<p>Git-backed snapshots. Replayable runs. The first time "sparrow broke my repo" was followed by
"sparrow rewind abc123" and laughter.</p>
<span class="pill shipped">shipped</span>
</div>
<div class="tl-item done">
<div class="dot"></div>
<div class="when">v0.3 — may 2026</div>
<h4>WebView cockpit & gateway</h4>
<p>Live dashboard on <code>:9339</code>. Telegram, Discord, Slack, WebSocket. Browser & computer-use tools.
The cockpit became a real surface.</p>
<span class="pill shipped">shipped</span>
</div>
<div class="tl-item">
<div class="dot"></div>
<div class="when">v0.4 — jul 2026</div>
<h4>Swarm mode</h4>
<p>Multiple sparrows, one task graph. Specialists that hand off context. <code>.agent.md</code> packs you can
install from a URL.</p>
<span class="pill next">next</span>
</div>
<div class="tl-item">
<div class="dot"></div>
<div class="when">v0.5 — q3 2026</div>
<h4>Long-running agents</h4>
<p>Background processes that survive reboots. Scheduled plans. Cross-project memory that actually makes
the second project easier than the first.</p>
<span class="pill soon">soon</span>
</div>
</div>
</section>
<section class="wrap">
<div class="quote">
<blockquote>
"Sparrow is what happens when someone who <b>actually maintains a repo at 2am</b>
builds an agent. Every feature is a scar from a real failure. <b>That's why it works.</b>"
</blockquote>
<cite>// the sparrow team, slightly tired</cite>
</div>
</section>
<section class="wrap" id="install">
<span class="sec-label">// one binary. thirty seconds.</span>
<h2 class="section">Install it. <span class="grad">Own it.</span></h2>
<p class="lead">No account. No API key required. Bring your own models, or use the cloud as a fallback
when you choose to. The whole thing is MIT.</p>
<div class="install">
<div class="install-bar">
<span>$ — one-click install — 92×24</span>
<span class="copy" onclick="navigator.clipboard.writeText('irm https://raw.githubusercontent.com/ucav/Sparrow/master/install.ps1 | iex\ncurl -fsSL https://raw.githubusercontent.com/ucav/Sparrow/master/install.sh | sh\nsparrow launch')">copy</span>
</div>
<div class="install-body">
<div class="p" style="margin-bottom:18px"># Windows PowerShell</div>
<div class="c">irm https://raw.githubusercontent.com/ucav/Sparrow/master/install.ps1 | iex</div>
<div class="p" style="margin-top:22px;margin-bottom:18px"># macOS / Linux</div>
<div class="c">curl -fsSL https://raw.githubusercontent.com/ucav/Sparrow/master/install.sh | sh</div>
<div class="p" style="margin-bottom:18px"># download pre-built binary</div>
<div class="c" style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:14px">
<a href="https://github.com/ucav/Sparrow/releases/latest/download/sparrow-linux-x86_64" style="display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border:1px solid var(--line);border-radius:7px;font-size:12.5px;color:var(--fg);text-decoration:none;background:#0a0805">🐧 Linux x64</a>
<a href="https://github.com/ucav/Sparrow/releases/latest/download/sparrow-macos-arm64" style="display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border:1px solid var(--line);border-radius:7px;font-size:12.5px;color:var(--fg);text-decoration:none;background:#0a0805">🍎 macOS ARM</a>
<a href="https://github.com/ucav/Sparrow/releases/latest/download/sparrow-windows-x86_64.exe" style="display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border:1px solid var(--line);border-radius:7px;font-size:12.5px;color:var(--fg);text-decoration:none;background:#0a0805">🪟 Windows x64</a>
</div>
<div class="p" style="margin-bottom:18px"># or from crates.io</div>
<div class="c">cargo install --git https://github.com/ucav/Sparrow sparrow-cli</div>
<div class="p" style="margin-top:22px;margin-bottom:18px"># or from source</div>
<div class="c">git clone https://github.com/ucav/Sparrow && <span style="color:var(--coral)">cd</span> Sparrow && cargo build --release</div>
<div class="p" style="margin-top:22px;margin-bottom:18px"># then</div>
<div class="c">sparrow launch <span style="color:var(--dim)"># first-run config, then WebView cockpit</span></div>
<div class="c">sparrow run "hello, world"</div>
</div>
</div>
<p style="text-align:center;color:var(--dim);margin-top:24px;font-size:13.5px">
Pre-built binaries for Linux / macOS / Windows · Source fallback requires Rust 1.96+ · Optional: Ollama, Docker, Playwright
</p>
</section>
<section class="wrap final-cta">
<h2>Fly <span class="grad">your own</span> agent.</h2>
<p>One star helps a lot. A "ship-it" issue helps more.
And if Sparrow saves your repo at 2am — tell someone.</p>
<div class="cta-row">
<a class="btn btn-primary" href="https://github.com/ucav/Sparrow">★ Star Sparrow on GitHub</a>
<a class="btn btn-ghost" href="https://github.com/ucav/Sparrow/blob/main/docs/cli-reference.md">Read the docs →</a>
</div>
</section>
<footer class="wrap">
<div class="foot">
<div style="display:flex;align-items:center;gap:10px">
<svg class="mascot" style="width:28px;height:28px"><use href="#sparrow"/></svg>
<span style="letter-spacing:2px;font-weight:600">SPARROW</span>
<span class="legal" style="margin-left:8px">v0.3.6 · MIT</span>
</div>
<div class="links">
<a href="https://github.com/ucav/Sparrow">GitHub</a>
<a href="https://github.com/ucav/Sparrow/blob/main/docs/cli-reference.md">Docs</a>
<a href="https://github.com/ucav/Sparrow/issues">Issues</a>
<a href="https://github.com/ucav/Sparrow/discussions">Discussions</a>
</div>
<div class="legal">// route · run · replay · rewind</div>
</div>
</footer>
<script>
const DOCS_INDEX = [
{ title: "First run", href: "#examples", tags: "setup run provider routing", body: "Configure providers and run a routed task with transcript and cost tracking." },
{ title: "Rollback after edits", href: "#examples", tags: "checkpoint rewind git safety", body: "List checkpoints and rewind the exact workspace tree after a bad edit." },
{ title: "Knowledge graph", href: "#examples", tags: "memory graph neo4j persistent sessions", body: "Persist entities and relationships locally, then optionally sync to Neo4j." },
{ title: "Browser/computer-use", href: "#examples", tags: "playwright browser computer screenshot click type", body: "Install Playwright and let gated tools screenshot, click, type, and inspect pages." },
{ title: "CLI API", href: "#api", tags: "api reference cli commands run plan", body: "sparrow run, plan, console, and memory graph command references." },
{ title: "Tool API", href: "#api", tags: "api reference tools browser computer knowledge_graph exec", body: "Engine tool names and capabilities exposed to models and surfaces." },
{ title: "WebView API", href: "#api", tags: "api reference webview endpoints slash websocket", body: "Local HTTP endpoints used by the cockpit, slash palette, and panels." },
{ title: "Video: first launch", href: "#videos", tags: "video tutorial setup providers onboarding", body: "Install, configure providers, run the first task, and open the cockpit." },
{ title: "Video: safe edits", href: "#videos", tags: "video tutorial checkpoint rewind permissions sandbox", body: "Permissions, checkpoints, approvals, and restoring after a bad batch." },
{ title: "Video: memory graph and browser", href: "#videos", tags: "video tutorial memory graph neo4j browser computer playwright", body: "Graph entities, Neo4j sync, screenshots, click and type actions." }
];
function scoreDoc(doc, query) {
if (!query) return 1;
const hay = `${doc.title} ${doc.tags} ${doc.body}`.toLowerCase();
return query
.split(/\s+/)
.filter(Boolean)
.reduce((score, term) => score + (hay.includes(term) ? (doc.title.toLowerCase().includes(term) ? 4 : 1) : -2), 0);
}
function renderDocsSearch(query = "") {
const host = document.getElementById("docsResults");
if (!host) return;
const q = query.trim().toLowerCase();
const rows = DOCS_INDEX
.map(doc => ({ doc, score: scoreDoc(doc, q) }))
.filter(row => row.score > 0)
.sort((a, b) => b.score - a.score || a.doc.title.localeCompare(b.doc.title))
.slice(0, 6);
host.innerHTML = rows.length
? rows.map(({ doc }) => `<a class="docs-result" href="${doc.href}"><div class="r-title">${doc.title}</div><div class="r-body">${doc.body}</div><div class="r-tags">${doc.tags}</div></a>`).join("")
: '<div class="docs-result"><div class="r-title">No result yet</div><div class="r-body">Try routing, browser, memory graph, API, or video.</div></div>';
}
document.getElementById("docsSearch")?.addEventListener("input", event => renderDocsSearch(event.target.value));
renderDocsSearch("");
document.querySelectorAll("[data-copy]").forEach(button => {
button.addEventListener("click", async () => {
const text = button.getAttribute("data-copy") || "";
try {
await navigator.clipboard.writeText(text);
const old = button.textContent;
button.textContent = "copied";
setTimeout(() => { button.textContent = old; }, 1100);
} catch {
button.textContent = "select";
}
});
});
</script>
</body>
</html>