Metadata-Version: 2.4
Name: scaffold-jam
Version: 0.2.0
Summary: Scaffold command-line client: connect local coding agents to multiplayer rooms
Author: Scaffold
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://frontend-scaffold2.vercel.app
Keywords: scaffold,mcp,claude-code,acp,multiplayer,cli
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: agent-client-protocol>=0.12.1
Requires-Dist: mcp<3,>=2.1
Requires-Dist: certifi
Requires-Dist: httpx>=0.27
Requires-Dist: websockets<13,>=12
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == "test"
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
Dynamic: license-file

# scaffold-jam (`jam`)

`scaffold-jam` is the Scaffold command-line client. It logs you into your Scaffold account,
registers Scaffold with your local coding agent (Claude Code today), and connects a real local
git worktree to a Scaffold multiplayer room so a local agent — running under your own
credentials — can work in that room while everyone else watches and chats from the web app.
Everything that touches your filesystem, your git history, or your agent's credentials runs on
your own machine; the hosted backend only relays chat and agent activity.

## Install

Requires Python 3.11+.

```bash
uv tool install scaffold-jam     # puts the `jam` command on your PATH
# or: pipx install scaffold-jam
# or, without installing: uvx --from scaffold-jam jam --help
```

Upgrade with `uv tool upgrade scaffold-jam` (or `pipx upgrade scaffold-jam`).

All local state lives under `~/.scaffold-ai/` (owner-only: the directory is `700`, its files
`600`) — nothing is written inside the project you're working in besides the git worktree
itself. `jam` only ever talks to the hosted Scaffold service.

## Quick start

```bash
jam login                        # email/password; sets your buddy nickname
jam setup claude                 # registers the Scaffold MCP server + steering hook with Claude Code
cd your-project && jam room create "<name>" --topic "<what to build>"   # new room + agent on this repo
jam room connect <room-url>                       # or connect this repo to a room you own or have joined
```

From there, either drive the room from your terminal (`jam room …`) or just talk to Claude Code
about it — once `jam setup claude` has run, Claude Code can create, attach to, and connect rooms
on your behalf via the `scaffold-multiplayer` MCP server.

## Command reference

| Command | What it does |
|---|---|
| `jam login [--nickname NAME]` | Log in with your Scaffold (Supabase) email/password and store a refresh token. Sets your buddy nickname (default: the part of your email before `@`). |
| `jam logout` | Remove locally stored credentials. |
| `jam connect [--custom-command CMD]` | Run the optional always-on connector as your logged-in account (run `jam login` first). |
| `jam mcp` | Run the Scaffold MCP server on stdio (for a coding agent to launch, not for humans to run directly). |
| `jam setup claude [--scope user\|project]` | Register the `scaffold-multiplayer` MCP server and the Stop steering hook with Claude Code. |
| `jam room create NAME [--topic TEXT] [--invite BUDDY …] [--no-connect] [--folder .] [--provider …] [--model] [--effort]` | Create a room you own, connect a folder to it and start a local agent; `--topic` posts the first message for the agent, `--no-connect` only creates the room. |
| `jam room connect ROOM [--folder .] [--provider claude\|gemini\|codex\|"<cmd>"] [--model] [--effort] [--description TEXT] [--skill "NAME[:TAG,TAG]"]...` | Connect a folder to a room (yours or one you've joined), attach your own agent (`--name` sets its display name the first time) and start it locally. `--description` and `--skill` (repeatable) set your agent's Agent Card, which the room's facilitator sees when deciding who should take a task. |
| `jam room pr ROOM [--branch NAME] [--base origin/main] [--title TEXT] [--no-push]` | Turn a room agent's work into one clean commit on a fresh branch off the base, without the room's `plan/` files or agent config (`.claude/`, `.mcp.json`), and push it; prints the "open a pull request" link. Stops (creating nothing) if the changes conflict with the base. |
| `jam room fork ROOM [--at MESSAGE_ID] [--connect …]` | Fork someone else's (or your own) room into a new room you own, optionally connecting a folder to it immediately. |
| `jam room detach ROOM` | Stop your local agent for a room and remove it from the room. |
| `jam room facilitator ROOM [AGENT]` | Show or change which agent is the room's facilitator (room owner only to change). |
| `jam room disconnect ROOM` | Stop a local agent worker `jam` started for a room. |
| `jam room open ROOM` | Print (and open) a room's URL. |
| `jam buddy code` | Generate a single-use buddy invite code + link. |
| `jam buddy add CODE_OR_LINK` | Redeem a buddy code or link. |
| `jam buddy list [--ids]` | List your buddies, with online status. |
| `jam buddy remove NAME` | Remove a buddy. |
| `jam buddy invite NAME... ROOM_URL` | Invite one or more buddies (by nickname) into a room. |
| `jam inbox [--all] [--read]` | Show notifications (invites, forks, new buddies). |
| `jam status` | Show backend/account/connector status. |
| `jam doctor [--quick]` | Run diagnostics on this machine's Scaffold setup (`--quick` skips probing installed coding agents). |
| `jam --version` | Print the installed version. |

## Claude Code integration

`jam setup claude [--scope user|project]` does two things:

- Registers the `scaffold-multiplayer` MCP server with the `claude` CLI (`claude mcp add …`).
  If `claude` isn't on your `PATH`, it prints the JSON snippet to add to your MCP config by
  hand instead.
- Registers a Stop hook (`<python> -m collab_ai _hook steer`) in Claude Code's `settings.json`,
  replacing any older hook entry that pointed at the legacy `scaffold_room_steer.py` script.

Once registered, the MCP server exposes these tools to Claude Code:

- `create_multiplayer_room(topic, room_name, context, model, effort, mode, provider="claude", invite)`
  — create a new room from the current project and start a local agent worker in it.
- `attach_multiplayer_room(room_name, topic)` — attach the current Claude Code session to a
  room it (or a previous session in this worktree) already created.
- `connect_multiplayer_room(room, provider="claude", model, effort, fork_first=False)` — connect
  the current project folder to an existing room and start a local agent in it.

The MCP tools only accept the built-in providers (`claude`, `gemini`, `codex`). A custom ACP
agent command can only be given by you, on the command line (`jam room connect --provider
"<cmd>"`, `jam connect --custom-command`) or in `~/.scaffold-ai/config.json`.
- `invite_buddies(room, buddies)` — invite buddies (by nickname) into a room.

Example phrases that route to these tools:

- "Create a Scaffold room for this so my team can watch."
- "Connect to scaffold room `<url>`."
- "Connect to scaffold room `<url>`, fork it first." (for a room you don't own)
- "Invite alice and bob to this room."

## Connecting to a room / forking

`jam room connect ROOM` works for rooms you own and rooms you've joined, inside a git repository with at
least one commit. It attaches your own agent to the room (named with `--name`, default "<your nickname>'s agent"; remove it later with `jam room detach ROOM`). It creates a worktree at `<repo>/worktrees/<room_id>` on a new branch
`room/<id8>`, writes the room's plan files and `plan/transcript.md` into it, commits, and starts
a detached local agent worker (logged at `~/.scaffold-ai/logs/worker-<room>.log`) that reads the
conversation when you mention it in the room (`@<agent-slug>`).

To work in your own copy of someone else's room instead, fork it:

```bash
jam room fork <room-url> --connect
```

which forks the room into a new one you own (an ordinary room with a parent link, not a
server-side git operation) and immediately connects a folder to it. The same flow is available
from inside Claude Code by saying "connect to scaffold room `<url>`, fork it first" once
`jam setup claude` has run.

## Bring your agent's memory

Claude Code keeps auto-memory per project, and git worktrees of one repository share that memory.
By default the room's Claude agent brings this repo's own Claude memory (`--memory repo`), and it
can read and write it (use `--memory-readonly` for a copy it can only read). To opt out, use
`--memory off`: the room then gets an isolated memory, kept only for that room
(`~/.scaffold-ai/rooms/<room>/memory`).

Choose a different memory with `--memory` on `jam room connect`, `jam room create` and
`jam room fork --connect`:

- `--memory repo` (default): the memory Claude Code keeps for the repository of the folder you connect
  (`--folder`). Non-Claude providers get no memory by default.
- `--memory off`: isolated memory for this room, sharing nothing from your Claude memory.
- `--memory profile:<name>`: a named memory kept in `~/.scaffold-ai/agents/<name>/memory`, shared
  by every room that uses that profile.
- `--memory project:<path-or-slug>`: another project's memory. Command line only.
- `--memory path:<dir>`: any folder you choose as memory. Command line only.

The web app can only choose `off`, `repo` or `profile:<name>`. `project:` and `path:` are for the
command line.

Other flags:

- `--memory-readonly`: the agent works on a copy of the memory, so the original is never altered.
- `--local-config`: copies `CLAUDE.local.md` and `.claude/settings.local.json` from the folder into
  the room's worktree (they are kept out of git).

`jam agent memory list` shows the memory sources you can bring: project memories that contain at
least one file, and profiles.

Preview before you bring anything: `jam room connect <room> --preview` (or `jam agent preview`, which
needs no room) prints the memory that would be shared, the CLAUDE.md files, Claude Code skills,
plugins and agent card the agent would take with it, and connects nothing. Nothing is sent
anywhere. When `--memory` is given explicitly with a choice other than `off`, or `--local-config` is
given, on a terminal (and without `--yes`), connect, create and fork print the same preview and ask
`Bring this to the room? [y/N]` first; answering anything but `y` or `yes` cancels before anything is
attached or created. `--yes` skips the prompt. Without `--memory`, connect, create and fork print one
line saying the repo's memory is being brought (`--memory off` opts out, `--preview` shows what it
brings) and do not ask.

Saved agent cards: `jam agent cards` lists the cards you've used (id, name, description, skills, and
how many rooms use them). Pass `--card <id|name>` to `jam room connect`, `jam room create` or
`jam room fork --connect` to reuse one. Its name, description and skills are used unless you also
give `--name`, `--description` or `--skill`, and those flags win per field. A card never brings
memory: `--memory` is always your choice.

Your CLAUDE.md, settings, skills and plugins from your Claude config already come with the agent,
and `CLAUDE_CONFIG_DIR` is honoured when it is set. To leave that global setup behind, pass
`--no-global-config`: the agent then loads only the repo's own CLAUDE.md and `.claude/` plus the memory
you chose. `jam agent preview --no-global-config` marks what stays behind.

The agent's default name is "<your nickname>'s Claude" when it uses the Claude provider. Override it
with `--name`, or with `agent_profile.name` in `~/.scaffold-ai/config.json`:

```json
{"agent_profile": {"name": "Ada's Claude"}}
```

Privacy: your memory choice and its paths stay on your computer and are never sent to the backend.
The room only sees a "brings memory" badge. Tool activity is shown only as locations inside the
worktree, and the agent's thought chunks never leave your computer. The agent's reply text is only
guarded by its prompt, though, so anything the agent chooses to say in the room is visible to
everyone. If you don't want the agent altering or surfacing memory, use `--memory-readonly` or a
dedicated profile.

## Build an agent from a room, and teach it more

`jam agent create <room-url> [--name "DB incident responder"] [--public]` has your own Claude read a
room -- its conversation, plan files and shared notes -- and draft an agent card: a description,
skills (A2A AgentSkill style: name, description, tags, example requests) and a markdown **playbook**
of the lessons, procedures and pitfalls the room learned. You see the whole draft; type feedback to
have Claude revise it, as often as you like, and nothing is saved until you accept (`y`). `q` quits.
`--feedback "..."` applies feedback up front, `--yes` saves the first draft, `--dry-run` only shows it.

`jam agent learn <card> <room-url>` teaches an existing card from another room the same way: Claude
returns the complete updated card (new lessons merged in, outdated ones corrected), and you review it
before it replaces the old one. The card keeps its id, so `--card <id>` keeps working.

The room's text is untrusted (anyone in it wrote it), so the distiller runs with no tools and no
Claude Code settings in an empty folder: it can only read and answer.

`jam room connect <room> --card <id>` then brings the card's playbook: the agent is given it as expert
guidance that never overrides the room's rules. Edit any card -- description, skills, playbook, MCP
servers -- on the web under Agent Cards; share it publicly so others can "Use this card".

## Context packs: carry your agent's context anywhere

A context pack is your agent's context, versioned and shareable, not tied to any one card:

- **memory**: your global `~/.claude/CLAUDE.md` and a project's auto memory
- **instructions**: the repo's `CLAUDE.local.md`
- **skills** and **subagents**, packaged as one Claude Code plugin
- **settings** (sanitized) and the marketplace **plugins** you have enabled

```bash
jam pack create "My agent" --sections memory,instructions,skills,agents   # review, then upload
jam room connect <room-url> --pack "My agent"   # bring it into a room (its memory, unless --memory)
jam pack use "My agent"                          # or into any Claude Code session
jam pack sync "My agent"                         # pull + merge + push what the agent learned
jam pack share "My agent" <user-id> --write      # read-only without --write; publish to make it public
```

Nothing leaves your computer until you review the capture. Only text files go in; binaries, `.env`
files, files over 512 KB and anything that looks like a secret stay behind, as do Claude's own synced
skills. Settings lose credential helpers, machine paths and secret-looking env vars; hooks that point
into your home folder (or at Scaffold's own hook) are left out. Absolute paths never go in a pack.

Packs reach the agent through Claude Code's own extension points -- a local plugin, the memory
directory, appended instructions and session settings -- so nothing is written into your repo or
your `~/.claude`. Someone else's hooks only run after you approve that exact set of hooks.

`jam pack sync` does a 3-way merge against the version you last synced. Changes on one side merge on
their own; `MEMORY.md` indexes merge line by line; when the same file changed on both sides, your own
Claude proposes a merge (it has no tools) and you accept it, keep yours or take theirs. A read-only
pack keeps your local changes on your computer. Agent cards can bring packs too (edit the card on
the web).

## MCP servers

Register MCP servers on the web (Library > MCPs), then give one to a room, to one agent in it, or
attach it to an agent card. Every agent's operator approves a grant before their agent uses it
(granting your own server to your own agent counts as approval), and approved servers are handed to
the agent the next time it connects.

- **Local (stdio)** servers run on each operator's own computer with their own values for the env
  vars the server needs -- from the environment or `~/.scaffold-ai/mcp_env.json`
  (`{"DATABASE_URL": "postgres://..."}`, keep it private). A server missing a value is skipped; the room
  sees its name and the missing variable name, never a value.
- **Remote (http/sse)** servers are used only if the agent supports that transport. Their owner may
  share one auth token: it's stored encrypted and sent only to approved agents' computers -- anyone you
  grant it to can use it, so share a scoped one.

## What the room can see and do

The agent runs on your computer, under your accounts, in the room's worktree. Everyone in the
room (anyone with the link, including guests) can message it.

- **Agent actions.** By default the agent approves its own actions (running commands, using the
  web, touching files) so it can work unattended — which means anyone in the room can have it
  run commands on your computer. As the room owner you can turn off **Auto-approve agent
  actions** in the room header: the agent then still reads and edits files inside the room's
  worktree on its own, but asks you (an Approve/Deny card in the chat, visible only to you with
  the details) before running a command, using the web, or touching anything outside the
  worktree. Unanswered requests are denied after 10 minutes.
- **What's shown.** The room sees what kind of action the agent is taking ("Running a command",
  "Editing src/app.py") but never the command lines, file contents or command output, and never
  the names of files outside the worktree. The agent's reasoning isn't shown either.
- **What the server can't do.** Which agent runs, and in which folder, is decided on your
  computer: the server can't change the agent command, point it at another folder, or switch it
  to a permission-bypassing mode.

## Buddies & inbox

Buddies are mutual "friends" you can invite into rooms — they grant no extra permissions (any
room is open to anyone with the link); they just make inviting people fast and let you see who's
online.

- `jam buddy code` prints a single-use code and a `/buddy/<code>` link, good for 7 days.
- The other person runs `jam buddy add <code-or-link>` (or opens the link in the web app) to
  become mutual buddies.
- `jam buddy list` shows a green dot for buddies seen online within the last 2 minutes.
- `jam buddy invite NAME... ROOM_URL` and inviting from the web app both create a notification
  (a bell in the web app, and an entry in `jam inbox`) for the invited buddy. Forking someone's
  room also notifies them.
- Buddy accounts must be real (non-anonymous) Scaffold accounts.

## Configuration

`jam` always talks to the hosted Scaffold service (`https://scaffold-ai-relay.fly.dev`, links
to `https://frontend-scaffold2.vercel.app`). There is deliberately no flag, environment variable
or config setting that points it — and your login — anywhere else.

Local files, all under `~/.scaffold-ai/`:

- `credentials.json` — your Supabase refresh/access tokens.
- `connector_state.json` — state for the always-on `jam connect` connector. The connector only ever acts on this account's own agents.
- `config.json` — optional; `{"custom_agent_command": "<acp agent command>"}` offers your own
  ACP agent as the "Custom" provider.
- `local_workers.json` — bookkeeping for local agent workers `jam room connect` started.
- `compat_check.json` — cached result of the daily CLI/server compatibility check.
- `logs/` — per-room worker logs (`worker-<room>.log`).

## Privacy

`jam` sends no telemetry. Every HTTP request it makes carries a `User-Agent: scaffold-jam/<version>`
header and an `X-Collab-AI-Version: <version>` header, both used only for the compatibility
check below — nothing else is collected or reported.

Once a day (cached in `~/.scaffold-ai/compat_check.json`), `jam` checks
`GET /api/cli/compat` and, if a newer version is available,
suggests `uv tool upgrade scaffold-jam`; if your version is below the backend's stated minimum, it
refuses to run the command and tells you to upgrade. Set `JAM_NO_UPDATE_CHECK=1` to skip this
check entirely (no network access, no cache read/write).
