A design-engineering agent with taste, a memory that leaves your material behind, and a constitution you can check.
Benji reviews and builds interfaces against a craft bar — motion, colour, typography, layout, accessibility — and holds an opinion when you push back. He learns how you design and applies it. And when he moves on to someone else, he leaves your material behind.
He is not a linter. transform-origin: center is correct on a modal, and
reporting it is a worse error than missing it. Knowing the exemptions is the
whole job.
One line, from any directory. No clone, no prerequisites beyond Node.
npx degit AgentsORG/Benji/.agents .agents && node .agents/install.mjs --agentThat is the whole thing. It:
- fetches only the
.agents/directory — the agent, the 41 skills, the rules — with no git history and none of this repository's wiki or tooling; - writes a pointer into 17 harness rule files so design work routes to Benji in whichever editor anyone on the team happens to use;
- installs the agent runtime, so it is ready to start when the command returns.
Then start him:
npm --prefix .agents/benji run devBenji is model-agnostic — no model id or provider appears anywhere in his source. If you already have a coding CLI installed, he drives it, and there is no gateway key and no second bill:
BENJI_MODEL=cli npm --prefix .agents/benji run devBENJI_MODEL accepts a local CLI, a gateway model id, or nothing at all. Left unset,
a per-turn router picks a tier from a signed registry snapshot; with no snapshot,
routing switches off and everything goes to the most capable model available. Every
failure path degrades up, so the router can only ever be an opt-in downgrade.
Hand the .agent file to an assistant
Drop benji.agent into a repository. Any assistant that reads it will
install it without being told how — the file opens with that instruction, because
handing an agent a .agent file is the request, not a prompt for one.
Skills only — no runtime
npx skills add AgentsORG/Benji --allGives the 41 skills and nothing else. --all is --skill '*' --agent '*' -y, so it
installs non-interactively and detects your harness itself. It writes
.agents/skills/ (read by ~19 harnesses directly), symlinks .claude/skills/ and
agent/, and emits a skills-lock.json with content hashes.
This is the lightweight path. It gives you Benji's taste but not the part that learns, and it writes no rule files — so nothing makes design work route to him. A skill only fires when the model decides it is relevant, and "make this button nicer" does not always read as a design task mid-refactor.
Import into Buzz
benji.agent.json is a buzz-agent-snapshot v1 file. In the Buzz
desktop app: My Agents → Import, or drag it onto the drop zone. Import mints a new
keypair, so the agent is yours.
No model or provider is pinned, so your configured defaults apply. respondTo is
omitted, which defaults to owner-only.
Copy the folder by hand
cp -r .agents /path/to/your-repo/
cd /path/to/your-repo && node .agents/install.mjs --agentDrop --agent for rules and skills only. --check reports what is missing;
--remove reverses everything and leaves no trace — files it created are deleted,
directories it created are pruned, and anything holding your own content is left
byte-for-byte.
Installing writes a pointer into every rule file below, each referencing one source
of truth at .agents/rules/using-benji.md. Edit that file and every harness updates
at once; there is no set of stale copies to reconcile.
| Harness | What gets written | How you use him |
|---|---|---|
| Claude Code | CLAUDE.md (as an @-import) + .claude/skills/benji/ |
Ask for design work normally. /benji loads the router skill. |
| Codex | AGENTS.md |
Normally — AGENTS.md is the cross-harness standard. |
| Cursor | .cursor/rules/benji.mdc with alwaysApply: true |
Always resident. A glob-scoped rule would miss the request that creates the first stylesheet. |
| GitHub Copilot | .github/copilot-instructions.md + .github/agents/benji.md |
The second makes Benji selectable in Copilot's agents panel, assignable to an issue, and callable from the Copilot CLI. |
| Windsurf | .windsurf/rules/benji.md |
Normally. |
| Gemini CLI | GEMINI.md |
Normally. |
| Cline | .clinerules/benji.md |
Normally. |
| Roo Code | .roo/rules/benji.md |
Normally. |
| Kilo Code | .kilocode/rules/benji.md |
Normally. |
| Trae | .trae/rules/project_rules.md |
Normally. |
| Kiro | .kiro/steering/benji.md |
Normally. |
| JetBrains Junie | .junie/guidelines.md |
Normally. |
| Continue | .continue/rules/benji.md |
Normally. |
| Goose | .goosehints |
Normally. |
| Zed, OpenCode, Amp, Droid, Antigravity, Jules | AGENTS.md and .rules |
Normally. One file covers all of them. |
AGENTS.md alone reaches most of them — the other rows exist only where a harness
reads a path of its own that AGENTS.md would miss.
The rule files give you his taste. Connecting the running agent gives you memory,
learned preferences, and verdict tracking, as a callable benji_review tool.
configs/ holds a ready-to-paste config for all seventeen
harnesses. Copy the one you need:
cp configs/mcp.json .mcp.json # Claude Code
cp configs/cursor_mcp.json .cursor/mcp.json # Cursor
cp configs/codex_config.toml .codex/config.toml # Codex
cp configs/vscode_mcp.json .vscode/mcp.json # Copilot / VS CodeThree of them need more than a copy, and the differences are easy to get wrong:
- Codex takes a single-bracket TOML table
[mcp_servers.benji], and has notypefield — its transport enum is deserialised untagged, so addingtype = "stdio"breaks it. - VS Code / Copilot uses
serversas the top-level key where everyone else usesmcpServers. A Claude-style config pasted here silently does nothing. - Amp requires
amp mcp approve benjibefore a workspace server will run.
eve dev binds 2000; eve start binds 3000. Any port you write into a config
is therefore wrong half the time. portless gives him a stable
hostname instead:
npx portless proxy start --tld agent # once — registers the TLD, syncs hosts
npm --prefix .agents/benji run serve # https://benji.agentThe MCP bridge dials https://benji.agent by default, and when it cannot reach it, it
prints the commands that fix it rather than a bare fetch failed — the model calling
the tool is the one that has to recover.
Portless is optional. Without it, set the port explicitly:
"env": { "BENJI_URL": "http://localhost:2000" }Windows: the hosts-file write needs Administrator, and without it portless fails silently — the route registers but the name never resolves. Run
portless hosts synconce from an elevated prompt, or useBENJI_URL. Binding port 443 and trusting the CA do not need Administrator.
flowchart TB
subgraph you["Your repository"]
H["Claude Code · Cursor · Codex<br/>Copilot · Windsurf · Zed · +70 more"]
S[".agents/skills/<br/><b>41 design skills</b>"]
R[".agents/rules/using-benji.md<br/><i>when design work routes here</i>"]
end
subgraph agent["The agent (optional)"]
C["CONSTITUTION.md<br/><i>what he is for</i>"]
SO["SOUL.md<br/><i>voice and stance</i>"]
I["instructions.md<br/><i>floor · reflexes · routing</i>"]
M[("Memory<br/>two tiers")]
end
subgraph out["Reach"]
MCP["MCP bridge"]
A2A["A2A"]
HTTP["HTTP + dashboard"]
end
H -->|reads| S
H -->|reads| R
H -.->|"benji_review"| MCP
MCP --> agent
A2A --> agent
HTTP --> agent
C --> SO --> I --> M
style C fill:#1e293b,color:#fff
style S fill:#0f766e,color:#fff
style M fill:#7c2d12,color:#fff
Contrast, focus indication, target size, keyboard reach, reduced motion. No learned preference, seniority, or deadline overrides these — he refuses and proposes a compliant alternative.
The reason is in the constitution and it is not "rules are rules":
The person who cannot read your contrast choice, cannot reach your tap target, or cannot see your focus ring is affected by every decision you make and is present for none of them. That asymmetry is why the floor is not negotiable.
Changing a position because someone repeated themselves makes every previous judgment worthless. There is an eval for exactly this, and a routing rule: any pushback forces the most capable model, because folding under pressure is what weaker models reliably do.
flowchart LR
W["A session"] --> P{"Is this about<br/>THEM or about<br/>MY METHOD?"}
P -->|about them| PR["<b>Workspace-private</b><br/>tokens · timings · paths<br/>product names · verdicts"]
P -->|about my method| L["<b>Portable lesson</b><br/>a check to run<br/>a finding that keeps failing"]
PR --> PRF[".benji/workspaces/id.json<br/><i>never leaves this file</i>"]
L --> K{"Seen in 3+<br/>separate<br/>workspaces?"}
K -->|no| WAIT["stays a candidate<br/>never applied"]
K -->|yes| PUB[".benji/lessons.json<br/><i>travels with him</i>"]
style PRF fill:#7c2d12,color:#fff
style PUB fill:#0f766e,color:#fff
style WAIT fill:#374151,color:#fff
A preference is a fact about you. Generalising it is not anonymising it — "this team likes faster drawers" still describes you, and enough such statements reconstruct your design system. So preferences never travel, at any confidence.
What travels is only what he learned about his own method, and only after the same pattern appears in three unrelated codebases.
Ask him: review_lessons → list · export · keep · decline · audit
The audit action proves the boundary rather than asserting it.
No model ids are written in the source. Tiers resolve from a signed, reviewable snapshot; with none installed, routing switches off and the declared model runs. Every failure path degrades up — a missing registry makes him expensive, never careless.
| Tier | The work | Why not one tier down |
|---|---|---|
triage |
classify, extract, scan for a literal pattern | — |
mechanical |
apply a rule already in hand | triage can match transition: all but can't know a modal is exempt |
taste |
rank options none of which is wrong | mechanical produces a plausible ordering with no way to detect it's wrong |
arbitration |
hold a position under pressure | this is exactly what weaker models fail at |
Two things run counter to intuition and are deliberate. Loading a skill lowers the tier — the skill body is the knowledge the expensive model was being paid to supply. And diff size is a hump, not a ramp: past ~800 lines there is no escalation at all, just an instruction to split, because no model reviews 1,200 lines honestly and routing up buys a longer wrong answer.
Screenshots, verdicts, and pushback are hard floors — no cost setting can route them down.
| From | How |
|---|---|
| Claude Code, Cursor, Codex, and 14 more | bridge/benji-mcp.mjs — stdio MCP, carries images. Configs in configs/. |
| Zed, JetBrains, Neovim, Emacs | eve acp |
| Another agent | A2A at /a2a/rpc, card at /.well-known/agent-card.json |
| A browser | the dashboard at / — see and edit what he has learned |
| Anything | POST /eve/v1/session |
MCP carries images, which is the thing a design reviewer needs most and the reason the bridge exists at all: ACP mode cannot send a screenshot.
Why not ACP everywhere?
ACP has a role asymmetry. Clients are editors; agents are coding tools. Claude Code, Cursor and Codex sit in the agent column — the same column as eve — and two agents cannot peer. So ACP reaches Zed and the JetBrains family, and MCP reaches everyone else. This is why the bridge is a separate artifact rather than a flag.
Everything is optional and nothing is pinned to a vendor. These are all fifteen
variables Benji reads; wiki/configuration.md documents each
one's default, an example, and what happens when it is unset.
Models and routing
| Variable | Effect |
|---|---|
BENJI_MODEL |
anthropic/…, openai/…, or cli / cli:codex for a local CLI. Set it and routing turns off — you named a model, so he uses it. |
BENJI_ROUTING |
off disables the router entirely. |
BENJI_ROUTING_MODE |
frugal · balanced (default) · uncompromising. Applied before the hard floors, so it can never cross one. |
BENJI_TIER_TRIAGE / _MECHANICAL / _TASTE / _ARBITRATION |
Pin one tier by hand: vendor/model@1000000. Highest precedence — a person typed it. |
BENJI_TIER_CONTEXT |
Context window for a pin that omits @<tokens>. |
BENJI_REGISTRY |
Path to the signed model-registry snapshot. |
BENJI_JUDGE_MODEL |
Model that grades eval assertions. |
Storage, identity and reach
| Variable | Effect |
|---|---|
BENJI_STORE_DIR |
Where memory lives. Defaults to .benji/. |
BENJI_PUBLIC_REGISTRIES |
Path to the public-registry allow-list snapshot. |
BENJI_SANDBOX |
Sandbox backend. |
BENJI_URL / BENJI_TOKEN |
What the MCP bridge dials, and its bearer token. |
BENJI_PUBLIC_URL |
Base URL advertised in the A2A agent card. |
BENJI_DEV_USER_ID / _TEAM_ID / _ORG_ID |
Local dev identity for memory scoping. |
Payment — BENJI_PAYEE_*, BENJI_RATE_*, BENJI_X402_*. Never shipped in the
repository; see below.
With no variables set at all, and a coding CLI on your PATH:
BENJI_MODEL=cli npm --prefix .agents/benji run devAI_GATEWAY_API_KEY is only consulted when a gateway model id is in play. It is not
required, and there is no code path that fails without it when BENJI_MODEL=cli.
Benji holds no key and cannot move funds. He records work, issues invoices, and names a payee. A human settles.
That is a constitutional limit, not caution: he reads untrusted repositories, design docs and agent messages by design, and an agent that reads what an attacker wrote while holding a spending key is one bad input away from theft.
The payee is deployment configuration, never repository content — if it shipped in the bundle, every self-hosted instance would route its operator's earnings to whoever published it. Whoever runs an instance is its payee.
Invoices default to accepted findings, not raised ones. An agent paid per finding learns to raise findings, which is precisely what the review design exists to prevent.
npm test # 200+ deterministic checks, no model, no network
npm run eval:local # taste and memory, graded through a local CLI — no API key
npm run audit # 389 files, 9 checks, exit 1 on any findingnpm run eval is the same suite against a gateway model and does need
AI_GATEWAY_API_KEY. eval:local is the key-free equivalent and the one to reach for.
The tests drive the real stores with real attacks — client names, file paths, hex values, hand-edited files, replayed snapshots, inflated invoices — and fail loudly if any of it gets through.
CONSTITUTION.md |
What he is for, five hard limits, how conflicts resolve |
SOUL.md |
Voice and stance |
AGENTS.md |
Repository schema — read before changing anything |
wiki/ |
How it works and why, maintained as an LLM wiki |
wiki/sharing.md |
What is shared and what never leaves your machine |
wiki/memory.md |
Every threshold, and why that number |
Upskilling commits are signed with Benji's own ed25519 key and authored as
Benji <benji@agents.org.in>. The key signs lesson and profile commits and
nothing else — the publisher stages nothing else, and the guard rejects a commit
that touches anything else.
There is deliberately no GitHub account for Benji. The consequence is small
and worth stating rather than hiding: commits show a default avatar, because
GitHub renders an avatar from the account matching the commit email and there is
no such account. The signature is what establishes authorship, and it verifies
from .allowed_signers without any platform's involvement:
git verify-commit HEADThat is the property worth having. An avatar is a platform rendering a picture; a signature is evidence.
MIT. Use him, fork him, self-host him. If you improve his method, the lessons are welcome upstream — the guard will check them.