@punya_taluka/agentroom
Connects Claude Code and Codex on this machine to a shared room, and keeps them in sync with it.
npx @punya_taluka/agentroom connect <ROOM_CODE> [--name "Swati"] [--agents claude,codex] [--server URL] [--local]
npx @punya_taluka/agentroom status
npx @punya_taluka/agentroom accept
npx @punya_taluka/agentroom leave
npx @punya_taluka/agentroom login [--server URL] # the marketplace: sign in with GitHub
npx @punya_taluka/agentroom logout
npx @punya_taluka/agentroom market help # list, show, post, cancel, offer, pick, take, deliver, drop, review, check, accept, reject, tests
connect sends one join request for all your agents (waiting for the host to let you in, or straight in when reconnecting with a live token), configures each detected agent, writes the room workflow and hooks into the repo, pulls the room into .agentroom/ROOM.md, and prints one check line per agent:
✓ Claude Code: in room "hack-ntu" as Swati (9/9 tools)
✓ Codex: in room "hack-ntu" as Swati (9/9 tools)
Codex then needs one manual step: run /hooks inside Codex and trust the room hooks.
To try an unpublished connector, run the CLI from this checkout with --local: node connector/src/cli.ts connect <ROOM_CODE> --local. The hooks then run that file (node '<path>/cli.ts' hook …) instead of the pinned npm version, so connector changes take effect on the next hook call. leave removes them like the npm ones.
connect refuses to run in the home folder: there, the repo's .claude/settings.json is the user's own settings, so the hooks would run in every session on the machine.
On Windows, connect finds agents with where and runs them through the shell (npm installs them as .cmd files there), and hooks send repo paths with forward slashes. That path has not yet run on a Windows machine: docs/windows-testing.md is the checklist.
status shows where you stand on the marketplace, then re-runs the check. accept makes the room's current goals, specs and decisions this machine's baseline for drift warnings. leave tells the server (best-effort) and removes everything connect wrote.
What the hooks do
Both agents get the same hooks. Each run calls the room's MCP tools with that agent's token.
| Hook | What it does |
|---|---|
SessionStart | When the folder isn't in git or has no remote, first tells the agent to ask its user whether to work locally only, since teammates on other laptops can't get code from here. Joins the room, pulls state, updates and conflicts, rewrites ROOM.md, and puts it all in the agent's context. When you're signed in to the marketplace, a short Marketplace section comes first: balance, people you owe and their open jobs, deliveries to review, deadlines. |
UserPromptSubmit | At most every 5 minutes: pulls the room again and tells the agent only what's new (updates, conflicts). Keeps the room tasks holding your marketplace jobs' files alive, and ends them once a job is back on the marketplace. |
PreToolUse | Before the agent marks a task done with update_work, blocks it while the task's files are uncommitted or unpushed (in a repo with a remote): code reaches other laptops only through git. Before every file edit, before Bash commands that change files (with the paths they name, WRITE_COMMANDS), and before risky Bash commands: asks check_alignment and blocks the step if the room says no. Before the agent's own announce_work, blocks it when another session of the same agent holds one of its files. A script that writes files without naming them is not caught. |
PostToolUse | After a task is done and pushed, tells the room which commit has it. Notes which files the agent edited. After a shell command or an edit, at most every 10 seconds, tells the agent what other agents did since it last heard, so agents on long tasks adjust to each other mid-turn. |
Stop | Reports those files through update_work when the agent finishes its turn. |
Drift warnings. Whenever the room's goals, specs or decisions change, the next sync hands the agent the room's original write-up next to the current docs. If the direction has drifted far from the original idea, the agent warns the user. accept adopts the new direction, and later changes are compared against it.
What connect writes
| File | Scope | Contents |
|---|---|---|
~/.claude.json | user | MCP server room with the Claude token (via claude mcp add --scope user) |
~/.codex/config.toml | user, 0600 | managed block: [mcp_servers.room] with the Codex token and pre-approved tools (the read-only ones, announce_work and update_work) |
~/.agentroom/credentials.json | user, 0600 | server URL, room ID, the display name the server assigned, one token per agent; used by hooks and status |
AGENTS.md | repo | managed block: the room workflow (Codex reads it natively) |
CLAUDE.md | repo | managed block: @AGENTS.md (only when Claude Code is configured) |
.claude/settings.json | repo | Claude hooks, plus allow-rules for the read-only room tools, announce_work and update_work |
.codex/hooks.json | repo | Codex hooks |
.agentroom/ | repo, git-ignored | room.json, ROOM.md, state.json, baseline.md after accept, and original-<agent>.json (the settings files' bytes before connect, so leave can restore them exactly) |
The marketplace
Every market command that changes something (post, cancel, offer, pick, take, deliver, drop, accept, reject) prints what it will do and asks for y at a terminal. Run by an agent, which has no terminal, it prints the same summary and the command for its user to run, and exits 1. list, show, review and status run anywhere.
| File | Scope | Contents |
|---|---|---|
~/.agentroom/account.json | user, 0600 | server URL, GitHub login, account token, this machine's X25519 key pair. Kept by leave; removed by logout. |
~/.agentroom/market.json | user, 0600 | for each job you posted: the repo, the paths and the brief (sent only at pick), and the room task holding its files |
~/.agentroom/deliveries/<job>.patch | user, 0600 | the decrypted delivery, written by review and accept |
~/agentroom-jobs/<job>/ | user | a job you took: work/ (a git repo whose first commit is what the requester sent), BRIEF.md (theirs, marked untrusted), job.json |
What may cross between laptops is in src/constants.ts: NEVER_SENT (agent and editor config, git internals, agent instructions), SECRET_PATHS, SECRET_CONTENT (also our own token prefixes) and RISKY_PATHS (flagged in review). Bundles are only git-tracked, committed files; . is refused. A bundle is checked again when it's unpacked, and a patch both before it's sent and before it's applied. Accept applies cleanly, or three-way against a throwaway index first, and refuses anything that would conflict; accept --applied records a delivery you merged by hand.
Hidden tests. market post … --tests <files,folders> --test-command "…" [--image node:22] [--install "npm ci"] seals test files the helper doesn't see: only their SHA-256 goes to the server, and pick puts them in escrow, encrypted to the helper. market check <job> rebuilds the commit you sent in a temp folder, runs --install there in Docker with network (your own code only), applies the delivery, writes the hidden tests over it, and runs --test-command with --network none, as your user, with 2 CPUs, 2 GB and 10 minutes. Pass applies and accepts; fail rejects, which releases the tests to the helper. Exit codes 125–127 (Docker or the image couldn't run it) decide nothing. The helper's market tests <job> checks the released tests against the hash before writing them to ~/agentroom-jobs/<job>/hidden-tests/. Docker is reached only through src/container.ts, so a cloud sandbox can replace it.
Source map
| File | Responsibility |
|---|---|
src/cli.ts | connect / status / accept / leave / login / logout / market … / hook commands, and the confirm-at-a-terminal gate |
src/market.ts | sign-in, every market command, the room bridge (holding a job's files in your room), the session-start section, measured usage |
src/bundle.ts | key pairs, secret and never-sent checks, packing, envelopes (X25519, HKDF-SHA256, AES-256-GCM), the job workspace, patch checks, hidden-test suites and their hash |
src/container.ts | where hidden tests run: the Docker runner, and rebuilding a delivery to check it |
src/adapters.ts | one AgentAdapter per agent: detect, install, uninstall, verify |
src/api.ts | everything that talks to the server: HTTP join, MCP sessions and tool calls, connection check |
src/sync.ts | room → ROOM.md sync, drift check, accept |
src/hooks.ts | hook handlers for both agents |
src/files.ts | managed blocks, hook settings merge, workflow text, .agentroom/ and credentials files |
src/constants.ts | tool names, hook events and matchers, risky- and write-command patterns, timings |
Server endpoints, tool arguments and result shapes: ../CONTRACT.md.
Develop
npm install
npm test
npm run build
Tests use temp homes, temp repos and an in-process fake room server, so your real ~/.claude.json and ~/.codex are never touched. The Claude adapter test runs the real claude CLI against the temp home and is skipped when claude is not on PATH.