install dev, and delete the template page rendering under the real one

`hanzo code` is the CLI's headline command and it runs `dev` by default. A
public `curl hanzo.sh | sh` did not install `dev`, so the first thing a new
reader ran answered:

  Error: the `dev` coding agent is not installed (not on PATH)

The two reasons the installer named for skipping it are both gone —
hanzoai/dev is public and publishes `dev-<os>-<arch>.tar.gz.sha256` — so it is
one row in TOOLS, exactly as LLM.md said it would be. With no second name: the
alias defaults to `hanzo-node`, which the CLI owns, and handing that name to a
different program is how the delegate silently changes identity. Verified end
to end in a clean HOME under both dash and bash: hanzo 1.9.18, hanzo-mcp
1.1.15, dev 0.6.91, and `hanzo code` now starts Hanzo Dev instead of erroring.

The page rendered four components, not one. Hero is the whole page and carries
its own header and footer, so the leftover Lovable-template Navbar, Features
and Footer rendered a SECOND fixed nav, four invented feature cards, sixteen
`href="#"` links that go nowhere, "Simplifying application development and
deployment with innovative container solutions" for a company that does not
sell containers, and a `© 2024` ten pixels under the real `© 2016–2026`. All
five leftover components deleted. Rendered proof: 1 header, 1 footer, 0 dead
links, 11 links and every one resolves.

Two JSX text nodes shipped their escapes as text — the page literally read
`source →` and `— the same build`. Fixed; 0 literal escapes render now.

llms.txt: `hanzo usage` is a group, not a command (it prints subcommands and
stops) -> `hanzo usage summary`. `hanzo engine serve enso` was published as if
it worked; it needs the Hanzo engine binary this installer does not ship, and
says so — stated as a gap rather than left to be discovered. Added the model
ids that exist, the public catalog URL, and why `hanzo login` is not a command.

The installer's version line printed the last field of `--version`, which is a
build date for `dev` (`✓ dev 2026-04-01`); it now takes the first token that
looks like a version. "installable today" is read off TOOLS instead of being a
second hardcoded list.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-01 13:39:23 -07:00
co-authored by Hanzo Dev
parent c861bcde5f
commit c19105c593
10 changed files with 92 additions and 311 deletions
+21 -9
View File
@@ -17,6 +17,11 @@ It installs, and this list is the whole list:
|---|---|---|---|
| `hanzo` | hanzoai/cli | `hanzo` | `hanzo-node` |
| `mcp` | hanzoai/mcp | `hanzo-mcp` | `mcp` |
| `dev` | hanzoai/dev | `dev` | — |
`dev` takes no second name. cli/install.sh defaults the alias to `hanzo-node`,
which the CLI already owns; giving it to `dev` too would leave the delegate name
pointing at a different program.
Both names of a pair are ONE build — a symlink, so they cannot drift. That
property is load-bearing for the CLI: cloud's control binary resolves
@@ -48,15 +53,9 @@ Named in the output rather than quietly substituted. An installer that reaches
for a package manager so the list looks complete is the exact defect this file
was rewritten to remove.
- **`dev`** — `hanzoai/dev` is private, so an anonymous `curl | sh` gets 404. It
also publishes no per-asset checksums. This is the one that hurts: `dev` is the
agent `hanzo code` runs by default, so a public install produces a CLI whose
headline command cannot run its own default backend (`hanzo code claude` and
`hanzo code codex` name other backends). Making that repo public and adding
`.sha256` siblings is all that is needed — then it is one row in `TOOLS`.
- **`node`** — `hanzoai/node` is private; same 404, no checksums. Its asset is
also a ~277 MB zip with vendored runtimes rather than a lone binary, so it does
not fit the one convention yet.
- **`node`** — `hanzoai/node` is private; a 404 to an anonymous fetch, and no
per-asset checksums. Its asset is also a ~277 MB zip with vendored runtimes
rather than a lone binary, so it does not fit the one convention yet.
- **`desktop`** — `hanzoai/desktop` is private, and its latest release is
linux-amd64 only with an orphaned macOS signature. `hanzo desktop` (a CLI verb)
is unaffected and works; the standalone Tauri app is what is unavailable.
@@ -183,6 +182,19 @@ The CR is committed INERT (empty tag, absent from `kustomization.yaml`).
Promoting an App with no image tag takes the host down instead of leaving it
alone.
## The page
`src/pages/Index.tsx` renders `Hero` and nothing else. `Hero` is the whole page
— it carries its own header and its own footer — so any sibling rendered beside
it is a second header and a second footer on the same screen. That is what
shipped for months: a leftover Lovable-template `Navbar`, `Features` and
`Footer` rendered under the real page, adding a competing fixed nav, four
invented feature cards, sixteen `href="#"` links, a "Simplifying application
development and deployment with innovative container solutions" strapline for a
company that does not sell containers, and a `© 2024` ten pixels below the real
`© 2016–2026`. All five leftover components are deleted; do not reintroduce a
component that renders chrome.
## Stack
Vite 5 + React 19 + Tailwind 4, one route (`src/pages/Index.tsx`). pnpm 9;
+23 -9
View File
@@ -42,17 +42,25 @@ CLI_REPO="${HANZO_CLI_REPO:-hanzoai/cli}"
# The binary is also the asset prefix and the name inside the tarball — the
# convention hanzoai/cli/install.sh relies on. Adding a tool is one line, the day
# its repo is public and publishes <binary>-<os>-<arch>.tar.gz.sha256.
#
# `dev` takes no second name on purpose. cli/install.sh defaults the alias to
# `hanzo-node`, which the CLI already owns — giving it to `dev` too would point
# the delegate name at a different program.
TOOLS='hanzo:hanzoai/cli:hanzo:hanzo-node
mcp:hanzoai/mcp:hanzo-mcp:mcp'
mcp:hanzoai/mcp:hanzo-mcp:mcp
dev:hanzoai/dev:dev:'
# The installable names, read off TOOLS so no message can name a different set
# than the loop installs.
tool_names() {
printf '%s\n' "$TOOLS" | while IFS= read -r n_row; do
printf ' %s' "${n_row%%:*}"
done
}
# Named, not silently skipped, and never substituted with something else.
#
# `dev` matters most: it is the agent `hanzo code` runs by default, so until its
# source is public `hanzo code` cannot run its own default backend. Saying that
# here beats letting someone discover it at the first `hanzo code`.
unavailable_rows() {
cat <<'ROWS'
dev|the agent `hanzo code` runs by default — source is not public yet
node|source is not public yet
desktop|`hanzo desktop` is in the CLI; the standalone app is not public yet
bot|`hanzo bot` is in the CLI; the standalone node is not native yet
@@ -103,6 +111,7 @@ With no tool named, installs every tool that ships as a public native binary.
tools:
hanzo the Hanzo CLI (also installed as hanzo-node, the same build)
mcp the MCP server (also installed as hanzo-mcp, the same build)
dev Hanzo Dev, the coding agent `hanzo code` runs by default
options:
-d, --dir PATH install directory (default: ~/.local/bin)
@@ -209,7 +218,12 @@ install_tool() {
FAILED_N=$((FAILED_N + 1)); FAILED_LIST="$FAILED_LIST $i_bin"
fail "$i_bin (nothing at $HANZO_DIR/$i_bin)"; return 1
fi
i_ver=$("$HANZO_DIR/$i_bin" --version 2>/dev/null | head -1 | awk '{print $NF}')
# The first token that looks like a version, not the last token on the line:
# `--version` output is not one shape across tools (`hanzo 1.9.18` ends on
# the version, `hanzo dev 0.6.91 (sha) 2026-04-01` ends on a date), and
# printing a build date where a version belongs is its own small lie.
i_ver=$("$HANZO_DIR/$i_bin" --version 2>/dev/null | head -1 \
| tr ' ' '\n' | grep -m1 -E '^v?[0-9]+\.[0-9]+' | sed 's/^v//')
[ -n "${i_ver:-}" ] || i_ver='?'
if [ -n "$i_alias" ]; then
@@ -294,11 +308,11 @@ main() {
done)
if [ -n "$why" ]; then
fail "$w — $why"
printf ' %sinstallable today: hanzo mcp%s\n' "$DM" "$N"
printf ' %sinstallable today:%s%s\n' "$DM" "$(tool_names)" "$N"
exit 1
fi
done
die "no such tool:$WANT — installable today: hanzo mcp"
die "no such tool:$WANT — installable today:$(tool_names)"
fi
if [ -z "$WANT" ]; then show_unavailable; fi
+22 -4
View File
@@ -18,12 +18,14 @@ That installs every Hanzo tool published as a public native binary:
- `hanzo` — the CLI (also installed as `hanzo-node`, the same build)
- `hanzo-mcp` — the MCP server (also installed as `mcp`, the same build)
- `dev` — Hanzo Dev, the coding agent `hanzo code` runs by default
One tool at a time:
```sh
curl -fsSL https://hanzo.sh/cli | sh # the CLI only
curl -fsSL https://hanzo.sh/mcp | sh # the MCP server only
curl -fsSL https://hanzo.sh/dev | sh # the coding agent only
```
Binaries land in `~/.local/bin`. Re-run to upgrade — installing is just
@@ -45,12 +47,9 @@ hanzo code "fix the failing test"
# Point an agent at the desktop and browser instead of the repo
hanzo desktop
# Serve a model locally on an OpenAI-compatible endpoint
hanzo engine serve enso
# Money and metering
hanzo billing balance
hanzo usage
hanzo usage summary
# Bring your own cloud
hanzo cloud list
@@ -60,11 +59,30 @@ hanzo cloud accounts
hanzo-mcp
```
A group name is not a command on its own: `hanzo usage` and `hanzo auth` print
their subcommands and stop. A first word the CLI does not recognise is read as a
task for the coding agent, so `hanzo login` starts a session about the word
"login" instead of signing you in.
`hanzo engine serve <model>` runs a model locally on an OpenAI-compatible
endpoint. It needs the Hanzo engine binary, which this installer does not ship;
with no `hanzo-engine` on PATH and no `HANZO_ENGINE_BIN` set it exits saying so.
## Models
Hanzo's own families, and the ids these tools take: `enso`, `enso-flash`,
`enso-ultra`, and `zen5`, `zen5-pro`, `zen5-coder`, `zen5-flash`, `zen5-mini`,
plus the single-purpose `zen-embedding`, `zen-rerank`, `zen-guard`, `zen-vl`,
`zen-image`, `zen-video`, `zen-voice`, `zen-music`, `zen-foley`. The live list is
`https://catalog.hanzo.ai/v1/models`, and the gateway is the sole authority on
it. `https://api.hanzo.ai/v1/models` answers the same question for a signed-in
caller and returns 401 without a bearer token.
## Links
- [Website](https://hanzo.sh)
- [Documentation](https://docs.hanzo.ai)
- [CLI source](https://github.com/hanzoai/cli)
- [MCP source](https://github.com/hanzoai/mcp)
- [Dev source](https://github.com/hanzoai/dev)
## Contact
- Support: [email protected]
-33
View File
@@ -1,33 +0,0 @@
import { Button } from "@/components/ui/button";
import { Apple, Play } from "lucide-react";
const Download = () => {
return (
<div className="py-20 px-4 bg-background relative overflow-hidden">
<div className="absolute inset-0 bg-accent/5 backdrop-blur-3xl"></div>
<div className="max-w-7xl mx-auto relative z-10">
<div className="text-center space-y-8">
<h2 className="text-4xl md:text-5xl font-bold text-white">
Get Started Today
</h2>
<p className="text-xl text-white/60 max-w-2xl mx-auto">
Download our app and experience the future of banking. Available on iOS and Android.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button className="px-8 py-6 text-lg bg-white text-black hover:bg-white/90 flex items-center gap-2">
<Apple className="w-5 h-5" />
App Store
</Button>
<Button variant="outline" className="px-8 py-6 text-lg border-white text-white hover:bg-white/10 flex items-center gap-2">
<Play className="w-5 h-5" />
Play Store
</Button>
</div>
</div>
</div>
</div>
);
};
export default Download;
-64
View File
@@ -1,64 +0,0 @@
import { Card } from "@/components/ui/card";
import { Code, Database, Bot, Share2 } from "lucide-react";
const Features = () => {
const features = [
{
icon: <Code className="w-6 h-6 text-white" />,
title: "Hanzo Code & App Builder",
description: "Build applications faster with our AI-powered code editor and visual app builder"
},
{
icon: <Database className="w-6 h-6 text-white" />,
title: "Hanzo Analytics & Base",
description: "Enterprise-ready infrastructure with real-time analytics and monitoring"
},
{
icon: <Bot className="w-6 h-6 text-white" />,
title: "Hanzo Dev AI Team",
description: "Your AI development team that helps write, review, and optimize code"
},
{
icon: <Share2 className="w-6 h-6 text-white" />,
title: "Open Source & Extensible",
description: "Built on open source technologies and designed for unlimited customization"
}
];
return (
<div className="py-20 px-4 bg-background relative overflow-hidden">
<div className="absolute inset-0 bg-accent/5 backdrop-blur-3xl"></div>
<div className="max-w-7xl mx-auto relative z-10">
<div className="grid md:grid-cols-2 gap-12 items-center">
<div className="space-y-6">
<h2 className="text-4xl font-bold text-white">
Accelerate Development with AI
</h2>
<p className="text-xl text-white/60">
Integrate powerful AI tools into your development workflow. From smart code suggestions to automated testing, Hanzo's AI-powered platform helps you build better applications faster.
</p>
</div>
<div className="grid gap-6">
{features.map((feature, index) => (
<Card
key={index}
className="p-6 hover-lift glass-effect flex items-start gap-4 group transition-all duration-300"
>
<div className="w-12 h-12 rounded-xl bg-accent/10 flex items-center justify-center group-hover:bg-accent/20 transition-colors">
{feature.icon}
</div>
<div>
<h3 className="text-xl font-semibold mb-2 text-white">{feature.title}</h3>
<p className="text-white/60">{feature.description}</p>
</div>
</Card>
))}
</div>
</div>
</div>
</div>
);
};
export default Features;
-70
View File
@@ -1,70 +0,0 @@
import { Github, Twitter, Linkedin } from "lucide-react";
import { Button } from "./ui/button";
const Footer = () => {
return (
<footer className="py-20 px-4 bg-background relative overflow-hidden">
<div className="absolute inset-0 bg-accent/5 backdrop-blur-3xl"></div>
<div className="max-w-7xl mx-auto relative z-10">
<div className="grid grid-cols-1 md:grid-cols-4 gap-12">
<div className="space-y-4">
<h3 className="text-2xl font-bold text-white">Hanzo</h3>
<p className="text-white/60">
Simplifying application development and deployment with innovative container solutions.
</p>
<div className="flex space-x-4">
<Button variant="ghost" size="icon" className="hover:bg-white/10">
<Twitter className="w-5 h-5" />
</Button>
<Button variant="ghost" size="icon" className="hover:bg-white/10">
<Github className="w-5 h-5" />
</Button>
<Button variant="ghost" size="icon" className="hover:bg-white/10">
<Linkedin className="w-5 h-5" />
</Button>
</div>
</div>
<div className="space-y-4">
<h4 className="text-lg font-semibold text-white">Platform</h4>
<ul className="space-y-2">
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Container Runtime</a></li>
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Platform</a></li>
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Base</a></li>
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Cloud</a></li>
</ul>
</div>
<div className="space-y-4">
<h4 className="text-lg font-semibold text-white">Company</h4>
<ul className="space-y-2">
<li><a href="#" className="text-white/60 hover:text-white transition-colors">About</a></li>
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Careers</a></li>
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Press</a></li>
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Blog</a></li>
</ul>
</div>
<div className="space-y-4">
<h4 className="text-lg font-semibold text-white">Legal</h4>
<ul className="space-y-2">
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Terms</a></li>
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Privacy</a></li>
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Cookies</a></li>
<li><a href="#" className="text-white/60 hover:text-white transition-colors">Licenses</a></li>
</ul>
</div>
</div>
<div className="mt-12 pt-8 border-t border-white/10">
<p className="text-center text-white/60">
© 2024 Hanzo. All rights reserved.
</p>
</div>
</div>
</footer>
);
};
export default Footer;
+19 -8
View File
@@ -32,6 +32,7 @@ const Hero = () => {
{ id: "curl", label: "Everything", cmd: "curl -fsSL hanzo.sh | sh" },
{ id: "curl-cli", label: "CLI", cmd: "curl -fsSL hanzo.sh/cli | sh" },
{ id: "curl-mcp", label: "MCP", cmd: "curl -fsSL hanzo.sh/mcp | sh" },
{ id: "curl-dev", label: "Dev", cmd: "curl -fsSL hanzo.sh/dev | sh" },
];
const [activeInstall, setActiveInstall] = useState("curl");
@@ -69,8 +70,8 @@ const Hero = () => {
<span className="bg-gradient-to-r from-[#d81c33] to-[#f0506a] bg-clip-text text-transparent">hanzo.sh</span>
</h1>
<p className="text-lg text-white/50 max-w-xl mx-auto">
The Hanzo CLI and MCP server, as native binaries. One command,
nothing to build.
The Hanzo CLI, the MCP server and the coding agent, as native
binaries. One command, nothing to build.
</p>
</div>
@@ -118,6 +119,7 @@ const Hero = () => {
{[
{ path: "/cli", label: "CLI Only" },
{ path: "/mcp", label: "MCP Server" },
{ path: "/dev", label: "Dev Agent" },
].map((shortcut) => (
<button
key={shortcut.path}
@@ -149,6 +151,14 @@ const Hero = () => {
icon: <Code className="w-4 h-4 text-green-400" />,
tint: "bg-green-500/10",
},
{
bin: "dev",
also: null,
desc: "Hanzo Dev \u2014 the coding agent `hanzo code` runs by default.",
href: "https://github.com/hanzoai/dev",
icon: <Terminal className="w-4 h-4 text-sky-400" />,
tint: "bg-sky-500/10",
},
].map((t) => (
<div key={t.bin} className="bg-white/[0.02] border border-white/10 rounded-xl p-5">
<div className="flex items-center gap-2.5 mb-3">
@@ -157,23 +167,24 @@ const Hero = () => {
</div>
<div>
<h3 className="font-medium text-white text-sm font-mono">{t.bin}</h3>
<a href={t.href} className="text-xs text-white/40 hover:text-white/60">source \u2192</a>
<a href={t.href} className="text-xs text-white/40 hover:text-white/60">source →</a>
</div>
</div>
<p className="text-xs text-white/50 mb-3">{t.desc}</p>
<p className="text-[11px] text-white/30">
also installed as <code className="text-white/50">{t.also}</code> \u2014 the same build, symlinked
</p>
{t.also && (
<p className="text-[11px] text-white/30">
also installed as <code className="text-white/50">{t.also}</code> — the same build, symlinked
</p>
)}
</div>
))}
</div>
{/* Not included \u2014 say so rather than let someone find out */}
{/* Not included — say so rather than let someone find out */}
<div className="bg-white/[0.02] border border-white/10 rounded-xl p-5">
<h2 className="text-sm font-medium text-white/60 mb-3">Not included yet</h2>
<ul className="space-y-1.5 text-xs">
{[
{ bin: "dev", why: "the agent `hanzo code` runs by default \u2014 source is not public yet" },
{ bin: "node", why: "source is not public yet" },
{ bin: "desktop", why: "`hanzo desktop` is in the CLI; the standalone app is not public yet" },
{ bin: "bot", why: "`hanzo bot` is in the CLI; the standalone node is not native yet" },
-50
View File
@@ -1,50 +0,0 @@
import { Button } from "@/components/ui/button";
import { LogIn } from "lucide-react";
const Navbar = () => {
return (
<nav className="fixed top-0 left-0 right-0 z-50 px-6 py-4 bg-background/80 backdrop-blur-lg">
<div className="max-w-7xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-2">
<svg
width="32"
height="32"
viewBox="0 0 61 61"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M20.2218 60.0521V40.0081H0V60.0521H20.2218Z" fill="white"/>
<path d="M0 40.0081L20.2218 41.9724V40.0081H0Z" fill="#D3D3D3"/>
<path d="M60.733 20.0039H20.2618L0.0805664 40.0079H40.4836L60.733 20.0039Z" fill="white"/>
<path d="M20.2218 0H0V20.004H20.2218V0Z" fill="white"/>
<path d="M60.7452 0H40.5234V20.004H60.7452V0Z" fill="white"/>
<path d="M60.7047 20.0039L40.5234 17.9995V20.0039H60.7047Z" fill="#D3D3D3"/>
<path d="M60.7452 60.0521V40.0081H40.5234V60.0521H60.7452Z" fill="white"/>
</svg>
<span className="text-2xl font-bold text-white">Hanzo</span>
</div>
<div className="hidden md:flex items-center space-x-8">
<a href="https://docs.hanzo.ai" className="text-white/80 hover:text-white transition-colors">Docs</a>
<a href="https://hanzo.ai/pricing" className="text-white/80 hover:text-white transition-colors">Pricing</a>
<Button
variant="outline"
className="text-white border-white/20 hover:bg-white/10"
onClick={() => window.location.href = 'https://console.hanzo.ai'}
>
Console
</Button>
<Button
className="bg-white text-black hover:bg-white/90"
onClick={() => window.location.href = 'https://auth.hanzo.ai'}
>
Sign in
<LogIn className="w-4 h-4 ml-2" />
</Button>
</div>
</div>
</nav>
);
};
export default Navbar;
-50
View File
@@ -1,50 +0,0 @@
import { Card } from "@/components/ui/card";
import { MessageSquare } from "lucide-react";
const Testimonials = () => {
const testimonials = [
{
quote: "The best banking experience I've ever had. Simple, fast, and secure.",
author: "Sarah Johnson",
role: "Business Owner"
},
{
quote: "This app has completely transformed how I manage my finances.",
author: "Michael Chen",
role: "Software Engineer"
},
{
quote: "Incredible user experience. Everything just works seamlessly.",
author: "Emma Davis",
role: "Financial Analyst"
}
];
return (
<div className="py-20 px-4 bg-background relative overflow-hidden">
<div className="absolute inset-0 bg-accent/5 backdrop-blur-3xl"></div>
<div className="max-w-7xl mx-auto relative z-10">
<h2 className="text-4xl font-bold text-white text-center mb-4">
What Our Users Say
</h2>
<p className="text-xl text-white/60 text-center mb-12 max-w-2xl mx-auto">
Join thousands of satisfied users who have transformed their banking experience
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{testimonials.map((testimonial, index) => (
<Card key={index} className="p-6 hover-lift glass-effect">
<div className="w-12 h-12 rounded-full bg-accent/10 flex items-center justify-center mb-4">
<MessageSquare className="w-6 h-6 text-white" />
</div>
<p className="text-white/80 mb-4 italic">{testimonial.quote}</p>
<div className="text-white font-semibold">{testimonial.author}</div>
<div className="text-white/60 text-sm">{testimonial.role}</div>
</Card>
))}
</div>
</div>
</div>
);
};
export default Testimonials;
+7 -14
View File
@@ -1,18 +1,11 @@
import Navbar from "@/components/Navbar";
import Hero from "@/components/Hero";
import Features from "@/components/Features";
import Footer from "@/components/Footer";
const Index = () => {
return (
<div className="min-h-screen">
<Navbar />
<Hero />
<Features />
<Footer />
</div>
);
};
// One route, one component. Hero is the whole page — it carries its own header
// and footer, so anything rendered beside it here is a SECOND header and a
// SECOND footer on the same screen. That is what used to happen: a leftover
// template Navbar, Features and Footer rendered under the real page, adding a
// competing fixed nav, four invented feature cards, sixteen `href="#"` links
// and a "© 2024" that disagreed with the copyright ten pixels above it.
const Index = () => <Hero />;
export default Index;