
Amq Cli
- 128 installs
- 79 repo stars
- Updated August 4, 2026
- avivsinai/agent-message-queue
amq-cli is a Claude Code skill for coordinating agents through the AMQ file-based inter-agent message queue.
About
amq-cli is a Claude Code skill for coordinating agents through the AMQ file-based message queue. It covers sending messages to another named agent, checking and draining the inbox, replying, inspecting receipts, setting up co-op mode, and joining a swarm team. A developer uses it when two or more agents (such as claude and codex) need to talk to each other during a task. It explains AMQ's AM_ROOT and AM_ME routing rules and how to resolve the mailbox root correctly.
- File-based message queue CLI (amq) for agent-to-agent coordination between claude and codex
- Handles sending, inbox draining, replies, receipts, priority, co-op mode, and swarm teams
- Manages routing via AM_ROOT and AM_ME env vars with a documented root-resolution truth-table
Amq Cli by the numbers
- 128 all-time installs (skills.sh)
- Ranked #3,719 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
amq-cli capabilities & compatibility
- Capabilities
- agent coordination · message routing · multi agent orchestration
- Use cases
- orchestration
- Runs
- Runs locally
- Pricing
- Free
What amq-cli says it does
Coordinate agents via the AMQ CLI for file-based inter-agent messaging.
AMQ primarily uses two env vars for routing: `AM_ROOT` (which mailbox tree) and `AM_ME` (which agent).
npx skills add https://github.com/avivsinai/agent-message-queue --skill amq-cliAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 128 |
|---|---|
| repo stars | ★ 79 |
| Last updated | August 4, 2026 |
| Repository | avivsinai/agent-message-queue ↗ |
What it does
Coordinating messages, replies, and lifecycle events between multiple named agents via the AMQ CLI.
Who is it for?
Multi-agent setups where named agents like claude and codex need to send messages, replies, reviews, and status updates to each other.
Skip if: Distributed systems design (RabbitMQ, Kafka), CI/CD pipelines, or single-agent tasks with no partner.
When should I use this skill?
You need to send a message to another agent, drain your inbox, set up co-op mode, or join a swarm team.
What you get
Reliable agent-to-agent messaging with correct root resolution and inbox handling.
- agent-to-agent message routing
- co-op and swarm coordination
By the numbers
- primarily uses two env vars (AM_ROOT and AM_ME)
- 5-row root-resolution truth-table
Files
AMQ CLI Skill
File-based message queue for agent-to-agent coordination.
AMQ manages the conversation, not the task plan. Use it for messaging, routing, replies, and adapter-emitted lifecycle events; keep work decomposition and execution in the orchestrator above it.
Prerequisites
Requires amq binary in PATH. Install:
curl -fsSL https://raw.githubusercontent.com/avivsinai/agent-message-queue/main/scripts/install.sh | bashEnvironment Rules
AMQ primarily uses two env vars for routing: AM_ROOT (which mailbox tree) and AM_ME (which agent). Getting these wrong means messages go to the wrong place or silently disappear, so it matters to let the CLI handle them rather than guessing.
Inside `coop exec` — everything is pre-configured. Just run bare commands:
amq send --to codex --body "hello" # correct
amq send --me claude --to codex ... # wrong — --me overrides the env
./amq send ... # wrong — use amq from PATHThe reason: coop exec sets AM_ROOT and AM_ME precisely for the session. Passing --me overrides the env, and passing --root intentionally overrides the current root (the CLI will note that on stderr if it differs from AM_ROOT). Prefer bare commands unless you mean to target a different root.
Outside `coop exec` — resolve the root from config, don't hardcode it:
eval "$(amq env --me claude)" # reads .amqrc chain, sets both vars
eval "$(amq env --session auth --me claude --export)" # pin this terminal to one session
# Or pin per-command without polluting the shell (useful in scripts):
AM_ME=claude AM_ROOT=$(amq env --json | jq -r .root) amq send --to codex --body "hello"Why not hardcode? The root path depends on the config chain (project .amqrc → AMQ_GLOBAL_ROOT → ~/.amqrc). Hardcoding skips this and breaks when the project moves or config changes. Use --export only when the whole terminal should stay pinned; it exports AM_BASE_ROOT for session roots and prints a stderr note. Treat it as one terminal, one session.
Global fallback: Orchestrator-spawned agents often start outside the repo root where no project .amqrc exists. Set AMQ_GLOBAL_ROOT or ~/.amqrc so amq env and amq doctor still resolve the correct queue.
Session pitfall: coop exec defaults to --session collab (i.e., .agent-mail/collab). Outside coop exec, the base root is .agent-mail (no session suffix). These are different mailbox trees — don't mix them up.
Root Resolution Truth-Table
| Context | Command | AM_ROOT resolves to |
|---|---|---|
Outside coop exec | amq env --me claude | resolved base root from project .amqrc, detected .agent-mail, AMQ_GLOBAL_ROOT, or ~/.amqrc |
Outside coop exec, no project .amqrc | amq env --me claude | detected .agent-mail in the current tree, otherwise AMQ_GLOBAL_ROOT or ~/.amqrc |
Outside coop exec, isolated session | amq env --session auth --me claude | <resolved-base-root>/auth |
Inside coop exec (no flags) | automatic | .agent-mail/collab (default session) |
Inside coop exec --session X | automatic | .agent-mail/X |
Task Routing
Before diving in, match the task to the right workflow — this avoids wasted effort:
| Your task | What to do |
|---|---|
| "spec", "design with", "collaborative spec" | Use /amq-spec instead — it has structured phase-by-phase guidance for parallel-research workflows. |
| Send a message, review request, question | Use amq send (see Messaging below) |
| Swarm / agent teams | Read references/swarm-mode.md, then use amq swarm |
| Received message with labels `workflow:spec` | Follow the spec skill protocol: do independent research first, then engage on the spec/<topic> thread — don't skip straight to implementation. |
Quick Start
# One-time project setup
amq coop init
# Per-session (one command per terminal — defaults to --session collab)
amq coop exec claude -- --dangerously-skip-permissions # Terminal 1
amq coop exec codex -- --dangerously-bypass-approvals-and-sandbox # Terminal 2Without --session or --root, coop exec defaults to --session collab.
Statusline (Claude Code)
To show the current AMQ session in your Claude Code status bar, add this snippet to your statusline script (e.g., ~/.claude/statusline.sh):
# AMQ session segment — try CLI first, fall back to env vars for older amq versions
amq_session=""
if _amq_out=$(amq env --session-name 2>/dev/null) && [ -n "$_amq_out" ]; then
amq_session="$_amq_out"
elif [ -n "$AM_ROOT" ] && [ -n "$AM_BASE_ROOT" ] && [ "$AM_ROOT" != "$AM_BASE_ROOT" ]; then
amq_session=$(basename "$AM_ROOT")
fi
if [ -n "$amq_session" ]; then
output+=$(printf " | \033[33mamq:%s\033[0m" "$amq_session")
fiamq env --session-name (v0.27+) prints the session name and exits 0 (empty when not in a session). The env-var fallback covers older versions. amq env --json also includes session_name.
To also set the terminal tab title (works in Ghostty, iTerm2, Terminal.app):
# Set tab title to "repo | amq:session" — re-asserts on each statusline refresh.
# Manual titles (e.g. Ghostty's prompt_tab_title) take priority and won't be overwritten.
tab_title="$repo_name"
[ -n "$amq_session" ] && tab_title+=" | amq:${amq_session}"
printf '\033]0;%s\007' "$tab_title" > /dev/tty 2>/dev/nullIntegration & Ops Quick Reference
# Global fallback for orchestrator-spawned agents
export AMQ_GLOBAL_ROOT="$HOME/.agent-mail"
# Symphony hooks
amq integration symphony init --me codex
amq integration symphony emit --event after_run --me codex
# Cline Kanban bridge
amq integration kanban bridge --me codex
amq integration kanban bridge --me codex --workspace-id my-workspace
# Runtime diagnostics
amq doctor --ops
amq doctor --ops --jsonDelivery Receipts
AMQ records delivery outcomes in consumer-local receipt files. The main stages are:
drained— a consumer successfully ingested the messagedlq— the message was moved to the dead letter queue during ingest
Use these when you need confirmation rather than just fire-and-forget messaging:
# Block on delivery for a single-recipient send
amq send --to codex --body "please review" --wait-for drained --wait-timeout 60s
# Query receipt history later
amq receipts list --me codex --msg-id <msg_id>
amq receipts wait --me codex --msg-id <msg_id> --stage drained --timeout 60samq read, amq drain, and amq monitor all apply the same strict header validation. Messages in inbox/new that are corrupt or have malformed headers are moved to DLQ and produce a dlq receipt.
Session Layout
By default, the root is .agent-mail (from .amqrc or auto-detect). Use --session to create isolated subdirectories:
.agent-mail/ ← default root (configurable in `.amqrc`)
.agent-mail/auth/ ← isolated session (via --session auth)
.agent-mail/api/ ← isolated session (via --session api)amq coop exec claude→AM_ROOT=.agent-mail/collab(default session)amq coop exec --session auth claude→AM_ROOT=.agent-mail/auth
The main env vars are AM_ROOT (where) + AM_ME (who). coop exec may also set AM_BASE_ROOT for cross-session resolution. The CLI enforces correct routing — just run amq commands as-is. Default .agent-mail/<session> layouts are recognized even without .amqrc; custom root names still need config or explicit flags/env.
Cross-Project Routing
Send messages to agents in other projects via --project or inline @project:session syntax. Requires peer configuration in .amqrc.
When to use `--session` vs `--project`: --session = same project, different session. --project = different project. Change one dimension at a time.
Peer setup
Add project and peers to your .amqrc:
{
"root": ".agent-mail",
"project": "my-project",
"peers": {
"infra-lib": "/Users/me/projects/infra-lib/.agent-mail"
}
}Both projects must register each other as peers for round-trip messaging.
Use `--project`/`--session` to route, not a raw `--root`. A direct --root selects which tree to operate on; it carries no sender-origin metadata, so the recipient can't reply (a naive reply loops back into their own tree). amq send therefore refuses an explicit --root that crosses into a different base tree than your active session (AM_ROOT/AM_BASE_ROOT) when no --project/--session/--from-session is given. To message another project replyably, register the peer and use --project (or inline @project). If a send is genuinely local, set the target as your AM_ROOT instead of passing --root.
Sending cross-project
# Flag syntax
amq send --to codex --project infra-lib --body "hello from here"
# Inline syntax (terser)
amq send --to codex@infra-lib:collab --body "inline syntax"
# Same session name as source (default when --session omitted)
amq send --to codex --project infra-lib --body "delivers to same session"Replies route automatically
When you receive a cross-project message, reply_project is set in the header. amq reply routes back automatically — no --project flag needed:
amq reply --id <msg_id> --body "got it" # routes back via reply_projectThread naming
- Same project P2P:
p2p/claude__codex - Cross-project P2P:
p2p/projA:collab:claude__projB:collab:codex - Topical (cross-project): use same thread ID across projects, e.g.,
decision/release-v0.24
For full details, see references/cross-project.md.
Cross-project identity (IMPORTANT)
When you receive a message where from matches your own handle (e.g., from: "claude" and you are claude), check from_project and reply_project. If either is present and names a different project, this is NOT an echo — it is a legitimate cross-project message from a different agent instance with the same handle. Process it normally.
AM_ROOT scoping after cross-project sends
After sending a cross-project message (via --project), your AM_ROOT still points to YOUR project. To send to your own partner (same project), use plain amq send --to codex — do NOT use --project. The --project flag is ONLY for sending to agents in OTHER projects.
Decision Threads
Decentralized decision protocol using existing AMQ primitives (no new CLI commands).
- Thread:
decision/<topic> - Kind:
decisionfor all messages - Labels:
decision:proposal,decision:objection,decision:support,decision:final; plusproject:<name>for cross-project decisions - Context on proposals:
{"proposal_id": "...", "question": "...", "options": [...], "required_projects": [...], "deadline": "..."}
Process: Propose → Review/Object → Resolve objections → Close when all required projects responded and no unresolved blocking objections.
amq send --to codex --project infra-lib --kind decision \
--labels "decision:proposal,project:my-project,project:infra-lib" \
--thread "decision/api-v2" \
--context '{"proposal_id":"api-v2","question":"Adopt new API?","required_projects":["my-project","infra-lib"]}' \
--body "Proposal: migrate to API v2. All tests green."Session-Aware Routing
Users refer to sessions using many words: "session", "stream", "squad", "team", "workspace", "channel", or just a bare name. When the user mentions sending to or talking to an agent in a named context (e.g., "ask codex on stream1", "send to the auth team", "talk to codex in squad-api"), you must discover sessions before routing.
Important: Do not confuse sessions with projects. "Project" in AMQ means a different repo/codebase (cross-project routing via --project). Sessions are isolated mailbox trees within the same project (via --session). If the user says "the infra project", that likely means --project infra, not --session infra.
# Step 1: Discover active sessions and agents
amq who --json
# Returns: [{"name":"collab","agents":[...]},{"name":"stream1","agents":[...]},{"name":"auth","agents":[...]}]
# Step 2: Match the user's name against session names in the output, then send
amq send --to codex --session stream1 --body "Message for stream1"Recognition patterns — any of these mean "route to a specific session":
- Explicit: "on stream1", "via auth", "in the api session", "the infra squad"
- Bare name: user just says "stream1" or "auth" — could be a session or an agent handle
- Colloquial: "team", "squad", "stream", "workspace", "channel" followed by a name
Note: The agent@name inline syntax (e.g., codex@infra) is for cross-project routing, not cross-session. For same-project session routing, always use --session <name> explicitly.
Rules: 1. When the user names something that could be a session, always run `amq who --json` first to check if it matches a known session name 2. If the name matches a session, use --session <name> on the send command 3. If it matches both a session and an agent handle, prefer the session interpretation when the user's phrasing implies a group/context ("on X", "in X", "the X team"), and the agent interpretation when it implies a person ("ask X", "tell X") 4. If the target session differs from your current session ($AM_ROOT basename), use --session <name> 5. Never guess — if the name doesn't appear in amq who --json output, tell the user (it may need coop exec --session <name> to initialize) 6. For cross-project routing (different repo), use --project instead — see Cross-Project Routing section
Messaging
amq send --to codex --body "Message" # Send (uses AM_ROOT/AM_ME from env)
amq drain --include-body # Receive (one-shot, silent when empty)
amq reply --id <msg_id> --body "Response" # Reply in thread
amq watch --timeout 60s # Block until message arrives
amq list --new # Peek without side effectsSend with metadata
amq send --to codex --subject "Review" --kind review_request --body @file.md
amq send --to codex --priority urgent --kind question --body "Blocked on API"
amq send --to codex --labels "bug,parser" --context '{"paths": ["src/"]}' --body "Found issue"
echo "evidence: tests green" | amq send --to codex --subject "done" --body - # - reads stdinBody is fail-closed. --body - (or --body @-, or omitting --body) reads stdin; a literal string or @file is used as-is. A send whose resolved body is empty/whitespace is rejected with a usage error instead of delivering a blank message — so --body - with nothing piped fails loudly rather than shipping an empty body. Pass --allow-empty only when you truly want a blank body (subject carries everything).
Send file paths, not file contents. When attaching source code, configs, or large text for review, send the file path in the message body, not the contents inline. The receiver can open the file with their local tools. If the receiver cannot access that worktree, send a short diff instead of the full source.
Filter
amq list --new --priority urgent
amq list --new --from codex --kind review_request
amq list --new --label bugOperator Gates
Almost all coordination is agent-to-agent. Occasionally the next required actor is a human: an approval, a manual test, a deploy only a person can run, or sign-off that a goal is complete. AMQ has no separate "gate" feature. You represent this structurally: address a message to the human's mailbox instead of describing the wait in prose to another agent.
The single invariant AMQ relies on here is recipient-as-next-actor: a message addressed to the human handle means a human is who must act next. Everything else below (the gate/<topic> thread name and the APPROVAL: / DONE: subject prefixes) is a naming convention that downstream tools like amq-noc watch for. AMQ routing and message classification do not special-case thread names or subject text; those are plain strings, useful only because humans and tooling agree to read them. They are conventions, not core AMQ semantics.
The human handle is user
By convention the human/operator mailbox is user. AMQ reserves this handle for validation in configured projects, so --to user is accepted wherever the project has a configured agent list. New co-op projects include user in the default agent set. For explicit amq init --agents ... projects or older roots, initialize the mailbox layout before relying on human drain/receipt/DLQ ergonomics:
# Seed the human mailbox alongside the agents (one-time, per project)
amq init --root .agent-mail --agents claude,codex,user
# or, for a coop project:
amq coop init --agents claude,codex,userThroughout this section, user means "the conventional human handle." In configured projects it is warning-free for strict handle validation; in older or explicitly seeded roots, make sure the agents/user/ mailbox exists before expecting a human to drain and reply from it.
Raising a gate
Use a stable gate/<topic> thread so a gate and its resolution stay together:
# Approval / choice / manual test a human must perform
amq send --to user --thread gate/<topic> --kind question \
--subject "APPROVAL: <decision>" \
--body "<what you need a human to approve or run, and why>"
# Human closeout of a completed goal (sign-off that the goal is done)
amq send --to user --thread gate/<topic> --kind decision \
--subject "DONE: <goal>" \
--body "<what was completed; what the human should confirm or close>"The human answers on the same thread from their own terminal or client, e.g. amq send --me user --to <agent> --thread gate/<topic> --kind answer --subject "APPROVED: <decision>" --body "<approval / answer text>" (use DENIED: or ANSWER: for a rejection or a plain answer). Reusing the gate/<topic> thread is what lets a watcher pair the answer with the open gate and clear it.
When NOT to raise a gate
Keep ordinary coordination agent-to-agent. Do not send to user for:
- FYIs and status updates ->
statuson a normal thread - Acknowledgements
- Routine code review between agents ->
review_request/review_response - Agent-owned blockers (waiting on another agent, a build, or a flaky test)
If the escalation owner is a lead/CTO agent, they decide whether to escalate, and that decision is agent-to-agent. But once a human action is actually required, it still becomes a to:user gate so tooling can observe it.
Anti-pattern
Prose like operator-held, pending operator, or manual approval inside an agent-to-agent message is not a gate. It is body text a human or tool has to guess at. If a human must act, address the human.
What a gate is, and is not
- It is an observability / handoff signal, not authorization or security. AMQ sender identity is local convention, not authenticated approval. A
to:usergate records that a human is the next actor; it does not grant permission or prove a human approved anything. Do not treat it as an access-control boundary. - Cross-session / cross-project gates must be intentional. Default examples target the human mailbox in the current session/project. Routing a gate to another session's or project's
useris a deliberate act (--session/--project), never the default. Gate-clearing is a consumer/orchestrator convention unless AMQ later adds explicit gate state.
Priority Handling
| Priority | Action |
|---|---|
urgent | Interrupt current work, respond now |
normal | Add to TODOs, respond after current task |
low | Batch for session end |
Message Kinds
| Kind | Reply Kind | Default Priority |
|---|---|---|
review_request | review_response | normal |
question | answer | normal |
decision | — | normal |
todo | — | normal |
status | — | low |
brainstorm | — | low |
References
For detailed protocols, read the reference file FIRST, then follow its instructions:
- references/coop-mode.md — Co-op protocol: roles, phased flow, collaboration modes
- references/swarm-mode.md — Swarm mode: agent teams, bridge, task workflow
- references/integrations.md — Symphony + Kanban integration commands, global root fallback, ops checks
- references/message-format.md — Message format: frontmatter schema, field reference
- references/cross-project.md — Cross-project routing: peer config, addressing, decision threads
- references/review-loop.md — Token-efficient review cycles: delegate multi-round reviews to background agents
Co-op Mode Protocol
Roles
- Initiator = whoever starts the task (agent or human). Owns decisions and receives updates.
- Leader/Coordinator = coordinates phases, merges, and final decisions (often the initiator).
- Worker = executes assigned phases and reports back to the initiator.
Default pairing note: Claude is often faster and more decisive, while Codex tends to be deeper but slower. That commonly makes Claude a natural coordinator and Codex a strong worker. This is a default, not a rule — roles are set per task by the initiator.
Phased Flow
| Phase | Mode | Description |
|---|---|---|
| Research | Parallel | Both explore codebase, read docs, search. No conflicts. |
| Design | Parallel -> Merge | Both propose approaches. Leader merges/decides. |
| Code | Split | Divide by file/module. Never edit same file. |
| Review | Parallel | Both review each other's code. Leader decides disputes. |
| Test | Parallel | Both run tests, report results to leader. |
Research (parallel) -> sync findings
v
Design (parallel) -> leader merges approach
v
Code (split: divide files/modules)
v
Review (parallel: each reviews other's code)
v
Test (parallel: both run tests)
v
Leader prepares commit -> user approves -> pushKey Rules
1. Initiator rule — reply to the initiator and ask the initiator for clarifications 2. Never branch — always work on same branch (joined work) 3. Code phase = split — divide files/modules to avoid conflicts 4. File overlap — if same file unavoidable, assign one owner; other reviews/proposes via message 5. Coordinate between phases — sync before moving to next phase 6. Leader decides — initiator or designated leader makes final calls
Stay in Sync
- After completing a phase, report to the initiator and await next assignment
- While waiting, safe to do: review partner's work, run tests, read docs
- If no assignment comes, ask the initiator (not a third party) for next task
Progress Protocol (Start / Heartbeat / Done)
- Start: send
kind=statuswith an ETA to the initiator as soon as you begin. - Heartbeat: update on phase boundaries or every 10-15 minutes.
- Done: send Summary / Changes / Tests / Notes to the initiator.
- Blocked: send
kind=questionto the initiator with options and a recommendation.
Modes of Collaboration (Modus Operandi)
- Leader + Worker: leader decides, worker executes; best default.
- Co-workers: peers decide together; if no consensus, ask the initiator.
- Duplicate: independent solutions or reviews; initiator merges results.
- Driver + Navigator: driver codes, navigator reviews/tests and can interrupt.
- Spec + Implementer: one writes spec/tests, the other implements.
- Reviewer + Implementer: one codes, the other focuses on review and risk detection.
Communication
- Use AMQ messages to coordinate between phases and report to the initiator
- Don't paste code blocks — reference file paths (shared workspace)
Interrupts
- Urgent messages labeled
interrupttrigger wake Ctrl+C injection + an interrupt notice (when wake is running).
Message Handling
amq drain --include-body— process incoming messagesamq send --to <partner>— send work/findings to partneramq send --to <partner> --wait-for drained --wait-timeout 60s— block on a single-recipient handoffamq receipts list --me <agent> --msg-id <msg_id>— inspect delivery historyamq receipts wait --me <agent> --msg-id <msg_id> --stage drained— wait for receipt arrivalamq reply --id <msg_id>— reply in thread
amq read, amq drain, and amq monitor all strict-validate headers before treating a message as successfully ingested. If a message in inbox/new is corrupt or malformed, AMQ moves it to DLQ and emits a dlq receipt instead of leaving it in place.
Spec Workflow
The spec workflow is a skill-managed protocol that uses standard AMQ kinds plus labels (workflow:spec, phase:*) on thread spec/<topic>.
Canonical spec phases are: Research -> Discuss -> Draft -> Review -> Present -> Execute
For the full spec protocol, see the amq-spec skill's spec-workflow.md.
Cross-Project Messaging
Send messages between agents in different projects. Requires peer configuration in .amqrc.
Peer Configuration
Each project's .amqrc maps peer names to their base root paths:
{
"root": ".agent-mail",
"project": "proj-a",
"peers": {
"proj-b": "/Users/me/projects/proj-b/.agent-mail"
}
}project: explicit self-identity (defaults to directory basename if absent)peers: name → absolute path to peer's base root
Critical naming rule: Peer keys must match the remote project's declared project name (or its directory basename if project is absent). Reply routing uses reply_project which is set to the sender's project identity — the receiver must have a peer entry with that exact name. If project A calls its peer backend but that project identifies as api-server, replies from api-server will fail because A has no peer named api-server.
Both projects must register each other as peers for round-trip messaging.
Addressing
Flag syntax (explicit)
amq send --to codex --project proj-b --body "hello"
amq send --to codex --project proj-b --session auth --body "to specific session"Inline syntax (terser)
amq send --to codex@proj-b --body "hello"
amq send --to codex@proj-b:auth --body "to specific session"Flags take precedence over inline syntax.
Session Defaults
--project proj-b without --session delivers to the same session name in the peer project. If your source root is .agent-mail/collab, the message goes to proj-b's .agent-mail/collab. Override with explicit --session.
Reply Routing
Cross-project messages carry reply_to (handle@session) and reply_project (project name). When you receive a cross-project message:
amq reply --id <msg_id> --body "got it"The CLI reads reply_project from the message, resolves the peer, and delivers to the correct project/session. The reply re-stamps reply_to and reply_project with the replier's own identity for continued round-trip.
Thread Naming
- Same project P2P:
p2p/claude__codex - Cross-session P2P:
p2p/collab:claude__auth:codex - Cross-project P2P:
p2p/proj-a:collab:claude__proj-b:collab:codex - Topical threads: Use the same thread ID across all participating projects (e.g.,
decision/api-v2,review/auth-module)
Safety
DeliverToExistingInbox: Cross-project delivery never creates directories in the peer project. The target inbox must already exist (created byamq initoramq coop initin the peer project). This prevents accidental scaffolding.- Peer paths must be absolute (or relative to
.amqrcdirectory). resolvePeervalidates the path exists before delivery.
Decision Threads
Decentralized decision protocol for cross-project coordination, based on RFC 7282 rough consensus. Uses existing AMQ primitives — no new CLI commands.
Process
1. Propose: Send a decision kind message on thread decision/<topic> with label decision:proposal 2. Review/Object: Participants reply with decision:support or decision:objection labels. Add blocking label for unresolved objections. 3. Resolve: Address blocking objections. Running code (tests) is stronger evidence than arguments. 4. Close: When all required projects have responded and no unresolved blocking objections remain, send decision:final.
Convention
# Proposal
amq send --to codex --project proj-b --kind decision \
--labels "decision:proposal,project:proj-a,project:proj-b" \
--thread "decision/api-v2" \
--context '{"proposal_id":"api-v2","question":"Adopt new API?","required_projects":["proj-a","proj-b"],"deadline":"2026-03-25"}' \
--body "Proposal: migrate to API v2. All tests green."
# Support
amq reply --id <msg_id> --kind decision \
--labels "decision:support" \
--body "LGTM. Tests pass on our side."
# Objection
amq reply --id <msg_id> --kind decision \
--labels "decision:objection,blocking" \
--body "Breaks backward compat for our consumers."
# Final decision
amq reply --id <msg_id> --kind decision \
--labels "decision:final" \
--body "Adopted with backward-compat shim. Shipping in v0.25."Context schema for proposals
{
"proposal_id": "api-v2",
"question": "Should we adopt the new API?",
"options": ["adopt", "defer", "reject"],
"required_projects": ["proj-a", "proj-b"],
"deadline": "2026-03-25",
"evidence": ["All CI green", "perf benchmarks attached"]
}What NOT to use cross-project for
- Same project, different session: Use
--sessioninstead - Swarm mode: Agent teams are project-scoped. Use AMQ bridge for swarm notifications within a project.
- Broadcasting: No
--to @allacross projects. Send individually to each peer.
Orchestrator Integrations
Use these commands when AMQ is the messaging layer underneath an external orchestrator.
AMQ's core transport is still the message. These adapters are intentionally narrow: they translate external lifecycle or task events into ordinary AMQ messages.
Root Resolution
For orchestrator-spawned agents, make the queue discoverable even when the process starts outside the repo root:
export AMQ_GLOBAL_ROOT="$HOME/.agent-mail"Or create ~/.amqrc:
{"root": ".agent-mail"}Root precedence:
flags > AM_ROOT > project .amqrc > AMQ_GLOBAL_ROOT > ~/.amqrc > auto-detectauto-detect covers the default .agent-mail layout in the current tree, including .agent-mail/<session> session roots without .amqrc. Custom root names still need .amqrc, explicit flags, or env vars.
Symphony
Lightweight optional hook adapter.
Patch WORKFLOW.md once:
amq integration symphony init --me codex
amq integration symphony init --me codex --checkEmit lifecycle events from hooks:
amq integration symphony emit --event after_create --me codex
amq integration symphony emit --event before_run --me codex
amq integration symphony emit --event after_run --me codex
amq integration symphony emit --event before_remove --me codexKnown limitation: init rewrites WORKFLOW.md through structured YAML/Markdown parsing, so frontmatter comments and formatting may be normalized.
Cline Kanban
Experimental bridge. Run it only if you are comfortable depending on a fast-moving preview WebSocket surface:
amq integration kanban bridge --me codex
amq integration kanban bridge --me codex --workspace-id my-workspaceDefaults:
- URL:
ws://127.0.0.1:3484/api/runtime/ws - Reconnect delay:
3s - Emits only on task session state transitions plus
task_ready_for_review
Runtime Diagnostics
amq doctor --ops
amq doctor --ops --jsondoctor --ops adds queue depth, oldest unread age, DLQ state, presence freshness, and integration hints on top of the base doctor checks.
Message Shape
Integration messages are self-delivered and carry metadata under context.orchestrator:
{
"orchestrator": {
"version": 1,
"name": "kanban",
"transport": "bridge",
"event": "task_ready_for_review",
"workspace": {
"id": "workspace-123",
"path": "/abs/path/to/worktree"
},
"task": {
"id": "task-42",
"prompt": "Review PR #47",
"column": "review",
"state": "awaiting_review",
"review_reason": "task_ready_for_review",
"agent_id": "codex"
}
}
}Common labels:
orchestratororchestrator:symphonyororchestrator:kanbantask-state:<state>handoffblocking
For the formal envelope and stability notes, see `docs/adapter-contract.md`.
Message Format Cheatsheet
AMQ messages are Markdown files with a JSON frontmatter header:
---json
{
"schema": 1,
"id": "<msg_id>",
"from": "claude",
"to": ["codex"],
"thread": "p2p/claude__codex",
"subject": "Optional summary",
"created": "<RFC3339 timestamp>",
"refs": ["<related_msg_id>"],
"priority": "normal",
"kind": "question",
"labels": ["bug", "parser"],
"context": {"paths": ["internal/cli/send.go"], "focus": "error handling"},
"reply_to": "claude@collab",
"reply_project": "my-project",
"from_project": "my-project"
}
---
<markdown body>Field notes:
schema: integer schema version (currently 1).id: globally unique message id (also the filename stem on disk).from: sender handle.to: list of receiver handles.thread: thread id string. For p2p, usep2p/<a>__<b>with lexicographic ordering.subject: optional short summary.created: RFC3339 timestamp.refs: optional list of related message ids (e.g., replies).priority: optional (urgent,normal,low).kind: optional (e.g.,review_request,review_response,question,answer,status,todo).labels: optional list of tags for filtering.context: optional JSON object for structured metadata.
Routing fields (set automatically by CLI — do not hand-craft):
reply_to: optional sender identity for routing replies (e.g.,claude@collab). Set on cross-session and cross-project sends.reply_project: optional sender project name for cross-project reply routing (e.g.,my-project). Present only on cross-project messages.from_project: optional sender project identity stamped on cross-project sends.
Notes:
- Don’t edit message files directly; use the CLI.
- The CLI auto-fills
id,created, and a defaultthreadwhen not provided. reply_to,reply_project, andfrom_projectare transport metadata stamped by the CLI.- Delivery outcomes are tracked separately in consumer-local receipt files.
drainedmeans the consumer ingested the message;dlqmeans ingest failed and the message moved to DLQ.
Integration Metadata
Messages emitted by amq integration ... commands store orchestrator-specific metadata under context.orchestrator.
Example:
{
"labels": ["orchestrator", "orchestrator:kanban", "task-state:awaiting_review", "handoff"],
"context": {
"orchestrator": {
"version": 1,
"name": "kanban",
"transport": "bridge",
"event": "task_ready_for_review",
"workspace": {
"id": "workspace-123",
"path": "/abs/path/to/worktree"
},
"task": {
"id": "task-42",
"prompt": "Review PR #47",
"column": "review",
"state": "awaiting_review"
}
}
}
}Label conventions:
- Always:
orchestrator,orchestrator:<name> - When state is known:
task-state:<state> - Review-ready handoffs:
handoff - Failed / interrupted work:
blocking
Token-Efficient Review Loops
When a review_request may take multiple rounds, do not keep the whole loop in the main conversation. Use your host's background worker or subagent primitive so the AMQ exchange runs in isolated context and only the final verdict returns.
Host Mapping
- Claude Code: use a subagent or background agent.
- Codex-based agents: use a spawned/background Codex worker or task.
- Tool names vary by host. The invariant is the same: intermediate AMQ rounds stay off the main thread.
Pattern
- The background agent sends the initial
review_requestviaamq send. - It waits for replies with
amq drain --include-body. - If the reviewer finds issues, it applies fixes and re-sends for review.
- It stops when the reviewer says the change is green or a max round count is hit.
- It returns one line to the main context, for example:
reviewer signed off after 3 rounds, 5 findings fixed.
Why
- Intermediate review rounds stay out of the main context.
- Repeated diffs, logs, and review notes do not accumulate as stale history.
- The main conversation keeps only the durable outcome.
Examples
Claude Code:
Agent({
run_in_background: true,
task: `
Send: amq send --to codex --kind review_request --body "Please review: src/foo.go"
Loop up to 3 rounds:
- amq drain --include-body
- if codex is green, stop
- apply the requested fixes
- amq send --to codex --kind review_request --body "Updated: src/foo.go"
Return one line only:
"reviewer signed off after 3 rounds, 5 findings fixed"
`
})Codex-style host:
Start a background worker/subagent for the AMQ review loop
- amq send --to codex --kind review_request --body "Please review: src/foo.go"
- amq drain --include-body
- apply fixes and re-send until green or max rounds
Return one line only:
"reviewer signed off after 3 rounds, 5 findings fixed"This is behavioral guidance for agents using AMQ, not a CLI feature or protocol change.
Swarm Mode: Agent Teams
Enable external agents (Codex, etc.) to participate in Claude Code Agent Teams by reading/writing the shared task list.
Commands
amq swarm list # Discover teams
amq swarm join --team my-team --me codex # Join team
amq swarm tasks --team my-team # View tasks
amq swarm claim --team my-team --task t1 --me codex # Claim work
amq swarm complete --team my-team --task t1 --me codex [--evidence '{"tests_passed":true}'] # Mark done
amq swarm fail --team my-team --task t1 --me codex --reason "tests red" # Mark failed
amq swarm block --team my-team --task t1 --me codex --reason "waiting on API" # Mark blocked
amq swarm bridge --team my-team --me codex # Run task notification bridgeCommunication
Communication is asymmetric — bridge delivers task lifecycle notifications only:
- Claude Code teammate → external agent: works directly via
amq send - External agent → Claude Code teammate: relay through the team leader's AMQ inbox
# External agent sends to leader, noting the intended teammate
amq send --to claude --thread swarm/my-team --labels swarm \
--subject "To: builder - question about task t1" --body "..."The leader drains and forwards via Claude Code internal messaging.
Bridge
amq swarm bridge watches the shared task list and delivers AMQ messages labeled swarm into the agent's inbox. Standard amq wake detects these automatically.
amq swarm bridge --team my-team --me codex --poll --poll-interval 5s &Task Workflow
1. amq swarm list — discover available teams 2. amq swarm join --team <name> --me <agent> — join a team 3. amq swarm tasks --team <name> — view available tasks 4. amq swarm claim --team <name> --task <id> --me <agent> — claim a task 5. Do the work 6. amq swarm complete --team <name> --task <id> --me <agent> [--evidence <json>] — mark done 7. amq swarm fail --team <name> --task <id> --me <agent> [--reason <str>] — mark failed 8. amq swarm block --team <name> --task <id> --me <agent> [--reason <str>] — mark blocked
Related skills
FAQ
How do agents send messages with AMQ?
With amq send --to <agent> --body '...' after AM_ROOT and AM_ME are set, typically via coop exec or amq env.
Is this a distributed message broker like Kafka?
No. It is a file-based queue for agent-to-agent coordination, explicitly not for distributed systems design like RabbitMQ or Kafka.