start
set up your repo
To see it first, agentmash.dev/how plays a made-up team of four in your browser: who is in which file, and what each agent is told before it writes.
Your team — connect once, sign in once:
Connect the repository. Someone who can push to it signs in with GitHub at agentmash.dev/install and picks it. That is all the repository needs: nothing is committed to it and there is no pull request. It gets one live room, and connecting it again — or a teammate connecting it — opens the same one.
Each developer, once per machine:
npx agentmash loginIt opens GitHub in the browser; after signing in there they type the code the terminal shows (
ABCD-EFGH), so a sign-in link sent to someone else gives its sender nothing. Then it puts AgentMash's hooks in their own Claude Code settings (~/.claude/settings.json, never a repository) and keeps a sign-in token in~/.agentmash/. That is their whole setup, for every repository they can push to that anyone on their team has connected — including ones connected later.It connects every other coding agent it finds on the machine the same way, in that agent's own settings and never a repository, with the MCP server beside the hooks: Codex (
~/.codex/hooks.json), Cursor (~/.cursor/hooks.json), and GitHub Copilot in VS Code and in its CLI (~/.copilot/hooks/agentmash.json). Devin needs nothing of its own: it runs the hooks in Claude Code's settings. The other agents it knows each get the generic hook in their own settings, and Continue and Zed their MCP server alone; which file each one gets is in docs/ADAPTERS.md, and Antigravity is connected only when asked for by name (npx agentmash tools add antigravity). It prints a line for each, connected, already connected or skipped and why;--skip codexleaves one out, andnpx agentmash toolslists them later, withtools add <id>andtools remove <id>to connect or take out one at a time. Codex runs no hook until you approve it: opencodex, type/hooks, and approve agentmash, once. The commands it approves never change from one version of agentmash to the next, so an upgrade does not ask again. Cursor drops what a hook adds to its agent's context, so there the warning before an edit comes as a refusal of the first edit of a file a teammate is in, with the advisory as its reason; the same edit again goes through.Restart Claude Code. From the next prompt on, every prompt (the first 500 characters) and every edit goes to the room, and the live view shows them. Before an agent edits a file a teammate's agent just changed, it is told who changed it, what changed and why. And when a teammate's agent is at work on the same branch, or in a file the session changed, the agent gets a short heads-up — when the session starts, and at a prompt, at most once every ten minutes and only when that is news.
Who gets into a room is decided by GitHub: anyone who could push to the repository when they
last signed in, if that was within the last seven days (AGENTMASH_PUSH_ACCESS_DAYS). After
that the room stays shut to them until they run npx agentmash login again, so someone removed
from the repository loses its room within the week. A hook in a checkout finds the repository from its origin remote, asks the
server which room it is connected to, and signs with the developer's own token — there is no
room key on the machine and no link to keep secret. A repository that nobody connected is left
alone, and so is one that carries its own install (below). npx agentmash status in a checkout
says who you are signed in as and whether that repository is connected; npx agentmash logout
takes it all back out.
A repository that carries its own install has a room already: the one init committed, which
every clone reports to. Connecting it on the website connects it to that room, never a second
one beside it, as long as the committed key opens a room on this server. If it was connected to
another room before, status, login and doctor in a checkout say "two rooms", and
npx agentmash connect there puts it right: it connects the repository to the committed room,
for everyone at once, with nothing to commit. A room it leaves that nothing was ever reported to
gives way without asking; one that has had anything reported to it, however long ago, only with
--replace. Either way it is never deleted, since a committed key may be in any clone, and it
keeps what is in it for its owners.
A key committed to a public repository is anyone's who reads it, so init, join and rotate
ask GitHub first, unauthenticated, whether each GitHub repository the checkout has a remote for
is public (one through an SSH host alias named for GitHub, like git@github-work:acme/app.git,
too), and stop when one is: connect it on the website instead, where only people who can push get in
and nothing is committed. --allow-public-key commits the key all the same, and says what anyone
who reads the repository can then read; with no GitHub remote, or no answer from GitHub, they go
ahead and say so in a line. For a key committed before that, the server asks GitHub the same of
every repository a room is connected to (at most AGENTMASH_GITHUB_CHECKS_PER_HOUR times an
hour, each repository at most once a day, and while a request waits only before a key
disconnects its room) and learns it from every sign-in's list too. A room disconnected before
GitHub answered stays judged by the repository it left until GitHub has. When the answer is
public, the room's key opens it only beside the sign-in of someone who can push there:
npx agentmash login on the machine, which the hooks, the MCP server and the CLI send beside the
key, or a browser signed in on the website for the dashboard. Anyone
else is turned away with 403 room_key_public, and the key stays that way if the repository goes
private later; npx agentmash rotate mints one that never was public. GitHub not answering
changes nothing. A room that was never connected to any repository can't be judged: its key
opens it on its own, wherever the key is, so connect the repository, or rotate.
Which repository that is goes by every remote of the checkout, not origin alone: in a fork,
origin is your own and upstream the team's, and a remote through an SSH host alias
(git@github.com-work:acme/app.git) names its repository by its path. With remotes for more
than one GitHub repository, connect binds only the one you name, npx agentmash connect acme/app, and init leaves it to you; it never picks your fork by itself. A fork connected on
the website gets a room of its own, never the one its copied config.json names, and connect
in a checkout of a fork is refused, with origin alone too: GitHub says which repositories are
forks when you sign in, and a fork's checkouts report to that room by its key anyway. And init on
a signed-in machine makes no room for a repository that is connected already: it says where its
room is, and --force then npx agentmash connect --replace is how to commit one all the same.
Or let Claude do it: agentmash.dev/setup is one prompt to
paste into Claude Code. It runs the login, checks the repository you are in, and tells you
what is left; the same prompt is plain text at /setup.md for an agent that fetches it.
Or commit it to the repository — the older way, for a server without GitHub sign-in:
npx agentmash initand the dashboard opens itself in your browser (--no-open to skip). Until someone's agent
has edited something it shows three things to do next, each with a copy button: make an edit,
commit the hooks so your team gets them by pulling, and turn diff streaming on. A bare
npx agentmash in a repo that is not wired up says the same in two lines.
npx fetches the published agentmash package
— no clone, no global install, Node 18 or newer. It creates a room, installs the hook
scripts and the MCP server with its launcher into .claude/agentmash/ (the package carries
its own copy of them), registers the hooks in .claude/settings.json without disturbing what's already
there, registers the MCP server in .mcp.json, writes the room config beside them, installs
the git post-commit client into this checkout, and prints your dashboard link. (If your repo
sets core.hooksPath to a directory in the working tree — .githooks/, husky, lefthook —
the git client is not installed: writing there would commit AgentMash to your whole team.
Nor when core.hooksPath comes from your global or system git config, a ~/.githooks every
repository runs its hooks from: a hook written there would run for every repository on the
machine. init says which, and carries on; --force-hooks-path overrides either, and a hook
put in a shared folder that way is taken out again only by uninstall in the repository that
put it there.) Commit it:
git add .claude .mcp.json && git commit -m "add agentmash" && git pushYour teammates: git pull, and approve the MCP server once.
The hooks, the MCP registration and the room travel with the repo, so their next Claude Code session joins automatically — nothing to install, no token to paste, no config to write. Claude Code picks up new hooks on its own; restarting the session is the sure thing. The one thing each teammate does by hand is answer Claude Code's prompt to approve the MCP server this repo registers; AgentMash does not answer it for them, and the hooks work either way — see How it reaches your teammates.
.git/hooks is not committed, so the git client is the one thing that does not arrive
with a pull. Each teammate runs npx agentmash git-hook install once; doctor says so
when it is missing. Teammates on Cursor run npx agentmash login, which connects it at user
level, or wire up the adapter
in the repository.
The dashboard is served by the coordination server itself, at the link init printed:
https://<your-server>/r/<room-id>. Nobody runs a build. A sidebar holds the pages —
live, people, changes, collisions (right now, and by place), history
(blocks of work, and the event feed) and settings — with the room itself at its foot.
Pages switch from the sidebar, with the keys 1–6, with ctrl/cmd+K (jump to any page, person
or file), or by hash (/r/<room-id>#history/24h, #collisions/places/7d). The window is in
the link as well as the page, so a link opens on the week you were reading and not on somebody
else's default; links from before the pages had these names (#board, #zones/7d,
#timeline/24h) still open the page they meant. On a phone the sidebar folds into a drawer.
Settings are read in the browser and changed in the repo. Each setting shows its value and,
behind change, the exact npx agentmash … command to run where the room key is. A link with
the key reads the room's own settings with the key in a header; a room opened by signing in
reads what its owner can see and says which values take the key.
Or sign in and skip the link. On a server with GitHub sign-in configured, /rooms lists
the rooms that belong to your account and opens each dashboard at /r/<room-ref> — the
non-secret ref, not the key — so there is no link to keep safe and nothing in the address bar
worth stealing. A room you connect on the website is listed for you and for everyone who can push to its
repository; one made with npx agentmash init joins the list when you paste its room id
into that page once. Signing in opens exactly what the dashboard reads (/activity,
/events, the live socket) and nothing else: reporting, rotating, purging and deleting still
take the room key. You stay signed in for 30 days and across deploys; the GitHub token that
came with the sign-in is held in memory for two hours and never written down, so connecting
another repository after that asks you to sign in again.
The CLI and the web installer write the same bytes — the same hook scripts, the same
settings.json merge, the same config.json — so it doesn't matter which one a team uses.
That is enforced by a test, not by care: the packaging tests install the built tarball into
a scratch directory and diff every file it writes against the server's own install plan.
Commands
| Command | What it does |
|---|---|
npx agentmash init | Create a room and wire up this repo |
npx agentmash join <id|url> | Point this repo at an existing room |
npx agentmash doctor | Check exactly what is and isn't working |
npx agentmash status | Who is working on what, from the terminal |
npx agentmash git-hook install | Install the post-commit client in this checkout |
npx agentmash git-hook uninstall | Remove it, restoring any hook it replaced |
npx agentmash rotate | Issue a new room key; the old one gets a grace period |
npx agentmash revoke | End that grace period now |
npx agentmash login | Sign this machine in with GitHub: AgentMash for every connected repository you can push to |
npx agentmash logout | Sign this machine out and take agentmash back out of every agent it connected |
npx agentmash tools [add|remove <id>] | The coding agents on this machine (claude, codex, cursor, copilot, devin, amp, antigravity, cline, crush, droid, gemini, goose, junie, kilo, kiro, opencode, openhands, qwen, continue, zed): found, connected, Codex's hooks approved; connect or take out one |
npx agentmash token <create|list|revoke> | Personal API tokens for the public API, made with this machine's sign-in; create prints the token alone on stdout |
npx agentmash hook --print-command --client <id> --event <event> | The line to register in a tool's own hook settings, for a tool agentmash has no connector for: the generic hook, in the dialect it reads |
npx agentmash connect [owner/name] [--replace] | Connect this repository, on the website, to the room it carries, so login and the website open it too; name it when the checkout has remotes for more than one |
npx agentmash disconnect | Stop this room opening to everyone who can push to the repository it is connected to |
npx agentmash login --room | In a committed install, prove this machine is you (optional) |
npx agentmash secure <off|any|github> | Require proven identities to write |
npx agentmash reconcile <approve|auto|off> | Whether a finishing session that overlaps a teammate's says so (it only ever advises) |
npx agentmash diffs <on|off> | Stream the changed text to the live view (off by default) |
npx agentmash audit | What was read and changed, by whom |
npx agentmash purge <prompts|events> | Delete what the room collected |
npx agentmash delete --yes | Delete the room outright |
npx agentmash uninstall | Remove the hooks and the MCP registration from this repo |
npx agentmash team add <name> --agents <a,b|all> | Put agents from the crew in this repo as a named team |
npx agentmash team remove <name> [--agents <a,b>] | Remove a team, or only the agents given |
npx agentmash team list [--json] | The teams installed in this repo |
npx agentmash team update | Rewrite the team files from the crew version this CLI carries |
npx agentmash mcp | Serve the MCP tools on stdio — the same server .mcp.json starts from .claude/agentmash/ |
The digest is not a command of this CLI, and not available to customers yet: it lives in its
own workspace (digest/) of the private AgentMash repository and is not published to npm —
see Weekly digest.
doctor is the one to reach for when someone's events aren't showing up. Because hooks
fail silently by design, it's the only thing that will tell you why — it resolves the
config and shows where each value came from, checks the hooks are registered, measures the
round trip and compares it against what a cold hook would need, validates the room,
reports whether the git client
is installed, and lists the teammates it can see.