Working draft. Companion to [[group_level]] design.
Updated as new commands emerge in the design conversation.
Re-read in full before every edit; keep it consistent with [[group_level]].
⚠ Project lifecycle superseded (2026-07): the project new / admit /
release flow below predates the certificate-based project model. Projects
are now a set of existing repos + machines + certified members, created
from the dashboard and managed with the identity commands in the next
section: see project_intra.md.
The Murmurent CLI manages the configuration that lets your local Claude Code instance act as a member of a murmurent-enabled group. It does not run agents: it manages agent installation, group membership, role assignments, projects, and the day-to-day verbs that touch group artefacts.
Design choice: a thin CLI is preferred over a GUI until a concrete onboarding need demands one.
All mutating commands write an audit entry to the group's lab-management repo.
The CLI uses your GitHub identity for attribution; assignments and approvals are signed where possible.
Where a command is restricted (PI-only, lab_manager-only), this is enforced by the lab-management repo's branch-protection rules, not by the CLI alone: the CLI's check is a first line of enforcement only.
For first-time setup as a new member, use murmurent onboard (see the Onboarding section below): it does install plus key generation, profile push, and PR.
For subsequent installs (e.g. on a second machine, or after a fresh OS) where the member is already registered:
murmurent install
Effects:
- Clones or updates the Murmurent repo (default ~/repos/murmurent).
- Creates ~/.claude/agents/ if absent.
- Symlinks frozen agents from the group registry; copies personal agents.
- Configures ~/.claude/settings.json with group-derived permissions and MCP servers.
- Initialises ~/.claude/agent-memory/ for the agents installed.
- Links the plain-English writing style into ~/.claude/output-styles/ (select it with /config → Output style).
- Turns off Claude Code's feedback survey in ~/.claude/settings.json, unless you set CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY yourself.
- Creates the Murmurent Dashboard icon (skip with --no-launcher). murmurent launcher re-creates it; murmurent launcher --remove deletes it; --user <handle> makes it always open as that handle.
murmurent install does not generate an age key or push a member profile: those are one-time onboarding events.
Stored in ~/.murmurent/machine.yaml. Each applies to this computer only.
Command
Effect
murmurent config repos-root [FOLDER]
Show, or set, the folder where this computer keeps its git repositories (default ~/repos). Setting it moves nothing
murmurent config project-layout [nested\|flat]
Show, or set, where new projects go: nested (one project folder holding its code, manuscript and seminar repositories; the default) or flat (each repository directly in the repositories folder). Existing projects are not moved
Show which version of each agent Claude Code will use, and where it comes from
One row per agent: centre version, your version or your agent (from your vault), project version (committed in the repo), local file (this machine only), broken link, or not installed. --repo describes a session started in that repo, whose own .claude/agents/ wins
murmurent agent list
List installed agents in ~/.claude/agents/
Each shown as linked (commons symlink), forked (personal copy), or user-file; forks also show upstream/local drift status
Replace a commons agent's symlink with an editable personal copy
Preserved across commons upgrades; canonical copy in the vault's agent_forks/ (rides murmurent vault sync to your other machines; legacy ~/.murmurent/agent_forks/ when no vault), hardlinked into ~/.claude/agents/. --force re-snapshots from the current commons. --project P keeps the copy in your vault's projects/P/agents/, for that project only
murmurent agent sync [--repo PATH] [--all]
Install the version of each agent that should win
The lab's into ~/.claude/agents unless you have your own; project and per-project versions into a repo; agents switched off there. See Choosing an agent's version
murmurent agent off <name> --project P / on
Switch an agent off (or back on) for you in one project
Becomes Claude Code's Agent(<name>) deny rule in the repo's .claude/settings.local.json. Guardians cannot be switched off
murmurent agent share <name> --lab / --repo PATH
Make the version you use the lab's (PI only) or a project's
--lab commits it to the lab-management repo's agents/; --repo copies it into .murmurent/agents/ for you to commit
murmurent agent drift [<name>]
Merge indicator for forks
Per fork: UP-TO-DATE / LOCAL-ONLY / UPSTREAM / DIVERGED / ORPHANED, plus a summary of forks needing review
murmurent agent unfork <name> [--force]
Restore the commons symlink, dropping the personal copy
Prompts if local edits would be lost, unless --force
murmurent agent relink
Re-link the vault's personal agents (symlinks) + forks (hardlinks) into ~/.claude/agents/
Run after a vault pull on another machine (scripts/setup.sh also runs it). Migrates a legacy ~/.murmurent/agent_forks/ into the vault once; idempotent and non-destructive
Personal preferences profile lives at ~/.claude/murmurent-preferences.yaml (local; never committed to group repos). Sets standardised cross-cutting fields once across all installed personal agents. See Tool preferences for the controlled vocabulary.
Command
Effect
murmurent preference show
Print the effective preferences profile
murmurent preference set <field> <value>
Set a standardised field; warns if <field> is not in the centre + guild vocabulary
murmurent preference unset <field>
Remove a field (agents fall back to registry default)
murmurent preference validate
Check the profile + every installed agent's defaults against the controlled vocabulary; prints warnings (typos, unknown enum values, missing-but-expected fields)
Fill in a group's post-creation details, written to its lab.md (repeatable --set; interactive without --set). Keys: github, github_org, notebook_host, notebook_path, slack_workspace, slack_invite_url, data_host, data_raw, data_refined. Setting github=<org>/<repo> also fills github_org when unset, clearing the "no GitHub org configured" warning
One project as a whole: its repositories, data folders, what it reads without owning, its keep-until date, when it was last used, and whether it is active, quiet or dormant. With no name, the project of the repository you are in. See Every repository is in a project
Set a project's data folders, what it reads without owning, and its keep-until date
murmurent project migrate [--apply] [--yes]
List every repository and every folder directly under immutable/ and append_only/ that no project claims, and propose project records for them (x, x_manuscript and x_seminar become one project x). Writes nothing without --apply, and asks before writing (--yes skips the question). Never moves, renames or deletes anything
murmurent project new <name> --charter <file> --members <list>PI/proposer
Create a new project
murmurent project members <name>
Print MEMBERS
murmurent project admit <name> <member>PI
Add a member
murmurent project release <name> <member>PI
Remove a member
murmurent project pause <name>PI
Mark inactive
murmurent project resume <name>PI
Mark active
murmurent project end <name> --reason <r>PI
Terminal event
murmurent project archive <name>PI
Archive repo + data
murmurent project gather <name> [--yes]
Move a project's repositories on this computer into one project folder. Shows the plan and moves nothing without --yes; never overwrites; lists anything to fix by hand
murmurent project provision-slack <name>PI
Create the project's private Slack channel, named proj-<name>, and invite its certified members. Idempotent
Back up every repo a project owns in one go, with the same per-repo safety
pre-flight as /murmurent-push (secret scan, secret-shaped names, governed-data
+ large-file guards). A repo that fails is blocked; the rest proceed. Exit codes:
0 all clean · 1 partial · 2 nothing pushable / project not found.
Drop a recorded mirror (the working clone's git remotes are left as-is)
murmurent project mirror list <repo> [--project <name>]
List a repo's recorded mirrors
Mirrors are extra push destinations beyond the primary origin — the
inter-group case where each lab's org wants its own copy. Each is managed as an
explicit mm-mirror-<slug> git remote, created/synced idempotently at push time;
origin is never touched, and no silent multiple-push-URLs are used, so each
destination's success/failure is reported separately ("also backed up to
labB: ✔ / ⚠ could not reach — tell your PI"). A mirror failure never rolls back
or blocks the primary push; it only downgrades the exit code to 1.
Authentication is whatever your git/gh already has for that org.
Terminal twin of the dashboard's Repos panel. All three commands
use the same core modules as the panel (core.repo_inventory,
core.adopt), so the two surfaces can't drift. "Murmurent-ready" and
"a project" are two different things: see
ready_vs_projects.md for that distinction.
Command
Effect
murmurent repo list [--host <name>]
Every git clone on this machine, each with its readiness verdict (✓ ready when it has a .murmurent.yaml marker, otherwise • not set up yet). Issue #94: this machine only, so remote hosts are skipped; view another machine's repos on its own dashboard (Remote dashboard access)
murmurent repo status <path-or-name> [--host <name>]
Is this repo murmurent-ready? A path is checked directly (local, or on --host over SSH); a bare name is searched on every registered machine. Ready means the .murmurent.yaml marker is present; .claude/agents/ plays no part. partial means only a legacy CHARTER.md setup (run repo upgrade). Also lists agent links that git tracks, with the repair (#150). Exit 0 = ready, 1 = not ready, 2 = not found (scriptable)
Make an existing clone murmurent-ready: writes the .murmurent.yaml readiness marker. --agents pins named agents to this repo as links (rarely needed: every agent works in every folder); those links are listed in the repo's .gitignore so they are never committed. Every repository belongs to a project, so when no project lists this one, adopt also records a one-repository project for it, with you as lead; an existing project is never changed. Adopting again changes nothing. See Every repository is in a project
Bring ready repos up to the current Murmurent release: converts legacy CHARTER.md bootstraps to the marker, migrates the marker schema, re-links commons agents, re-stamps bootstrap_version. Agent content updates never need this: symlinks track the commons clone automatically
Experiments follow the lab project structure: exp/<integer>_<slug>/ in the project repo, with immutable and append-only data on the lab VM.
Command
Effect
murmurent experiment new --project <project> --name <slug>
Scaffold exp/<next-int>_<slug>/ with README.md, run_all.py skeleton, notebook.md template; create $MURMURENT_DATA_ROOT/immutable/<project>/<exp>/ and $MURMURENT_DATA_ROOT/append_only/<project>/<exp>/; open notebook.md in Obsidian
murmurent experiment list [--project <project>]
List experiments and their status
murmurent experiment status <project> <slug> --set <state>
Classify files in <source> as raw vs derived (instrument profile + generic patterns), present for mandatory review, then copy: raw → $MURMURENT_DATA_ROOT/immutable/<project>/<slug>/ (chmod a-w), derived → .../append_only/<project>/<slug>/instrument_outputs/. Compute SHA-256; update immutable_data:, instrument_outputs:, checksums: in notebook.md.
The last_updated field is set by the MCP on every write, by a pre-commit hook for direct edits, and by a GitHub Action on push as a backstop for web-UI edits.
Start a choreography from nothing: create its repository, make it ready, declare it, pose its question, commit, and ask the PI to make it a project. Asks for anything left out; --yes asks nothing. See Starting a choreography
Start a long job inside tmux (so a dropped connection does not stop it) at priority nice 19, with thread limits set and cores and GPUs chosen from the lab's rules and the server's load. Writes a job card under ~/.murmurent/runs/ and posts one line to the project's Slack channel when the job finishes, fails or stalls (never the command or a file path)
murmurent run --cores N --gpus N --memory 64G
Ask for cores, GPUs and memory. Requests above the lab's allowance are capped or refused, and the reply says why
murmurent run --out <folder> --expect-disk 500G
Check the output folder's free space, and refuse a job whose stated output would not fit
murmurent run --input <path> [--input <path> ...]
Record what the job reads, so the space scan can tell later whether its output could be made again
murmurent run --at 22:00 / --when-load-below 150 / --wait
Start later: at a time, once the server is quieter, or as soon as there is room
murmurent run --exception "<reason>" --until <time>
Go above your allowance after asking in the server channel; recorded on the card
murmurent run --session <name>
Put the job in a new window of an existing tmux session
murmurent run --no-notify / --dry-run
No Slack line / show what would be chosen and start nothing
murmurent jobs [--all]
What is running, waiting and recently ended, with estimates
murmurent jobs show <id or name>
Everything recorded about one job
murmurent jobs stop <id or name>
Stop a job: asked to end cleanly, then by force 30 seconds later
murmurent jobs policy [--init]
The lab's rules in force; --init creates policy/millwright.yaml from the template if the lab has none
murmurent etiquette check [--notify]
Lab members running above nice 19, over their cores or GPUs
Look through one project's repositories and data folders and list what could be deleted, compressed, moved or asked about, most pressing first. Reads sizes, dates, owners and names; changes nothing. The table is kept in ~/.murmurent/space/, readable by you only
murmurent space show [project] [--category C] [--owner O] [--min-size 10G] [--all]
The table from the last scan: emergencies, the top three, then every row waiting for a decision
murmurent space decide <row> keep\|delete\|mothball\|compress\|move\|undo [--note …]
Record your decision on one row. Nothing is carried out
murmurent space gaps [project]
Cases the lab's procedures do not cover, each with an example
murmurent space scan-lab [--notify]
(PI, on the server) Scan every project's data folders and the folders no project claims; write the lab report; with --notify, send the PI a Slack summary without file names
murmurent space lab [--host <server>] [--json]
The lab's last report: emergencies, what to look at first, every project. With --host, fetch it from the server over SSH
murmurent space schedule [--install\|--remove] [--day sun --at 02:00]
Show, set up or remove the weekly lab scan (a systemd user timer); recorded in the lab records repository
Open the local dashboard (the FastAPI web UI) in your browser
murmurent dashboard --stop [--port N]
Stop the dashboard running on this machine
murmurent dashboard --pi
Open the PI view (rejected if you're not a PI); adds escalated-from-members nags
murmurent dashboard --snapshot
Print the latest markdown snapshot from the lab-management repo
murmurent dashboard --outstanding
Print only the Outstanding analysis panel (terminal-friendly summary)
murmurent dashboard --tunnel you@host
Open an SSH tunnel to you@host and forward its dashboard to http://localhost:8770 on this machine. A literal you@host or ~/.ssh/config alias works directly — no registration needed (a name in hosts.yaml is honoured as an optional fallback). See Remote dashboard access
One-shot setup for a new member: clone Murmurent, install agents per profile, configure MCP, generate age key, push key + member profile via PR
murmurent doctor
Check the local install and print the one command that fixes each problem: Python version, whether pip/python on PATH match the interpreter murmurent runs under, agent/rule/skill links into the commons (including dangling ones), registered hooks and their interpreter, whether a clone's origin can still git pull, and whether the Murmurent Dashboard icon exists. Exit 0 = healthy (warnings allowed), 1 = something failed
murmurent offboard --member @<handle>PI
Mirror of onboard: revoke roles, release from projects, archive age key, mark profile alumni
$ murmurent onboard newmember --profile student
Cloning murmurent... done.
Installing agents (profile: student): bookworm, adversary, blacksmith, artist
bookworm: personal (copied)
adversary: frozen (symlink)
blacksmith: personal (copied)
artist: personal (copied)
Configuring MCP servers: inventory, oracle, request_board
Generated age key. Public key: age1...
Opened PR #87 in lab_mgmt: "Onboard @newuser"
# (PI approves PR and runs `murmurent project admit` for each project in onboarding issue)
$ murmurent doctor
All checks passed.
$ murmurent pull brca_imaging
# work in Claude Code on member/the_pi/qc-batch-3 ...
$ murmurent audit src/qc.py
$ murmurent push brca_imaging --message "add QC for batch 3"
Pushed to member/the_pi/qc-batch-3 (direct; no review).
# later, when ready to merge:
$ murmurent push brca_imaging --finalize
Opened PR #14: member/the_pi/qc-batch-3 → main
Triggered: adversary (src/**), security_guard (always)
provision is no longer a CLI command. From inside CC:
> what's missing for tomorrow's titration?
[CC calls inventory_provision via the inventory MCP]
Missing: anti-CD31 (no stock)
Expired: 4-OHT (expiry 2026-05-04)
OK: 3 items in stock and within shelf life
$ murmurent publish notes/abcb1_finding.md --to group
Published to lab group oracle as findings/2026-05-05_abcb1.md
Audit entry written to lab_mgmt/oracle-publish.log
$ murmurent role assign lab_manager @member_b
Opened transition issue #42 in lab_mgmt
Awaiting acknowledgement from @member_b and handoff from @prev_admin.