From agent-talk
Sets up or resumes a session's agent-talk user identity from existing global or project-local users, or creates a new one. Gathers relay, passphrase, and peer info for autonomous send/receive.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agent-talk:initThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
agent-talk keeps users in two scopes; the agent manages both (nothing for you to
agent-talk keeps users in two scopes; the agent manages both (nothing for you to configure):
~/.agent-talk/users/<name>/<project-root>/.agent-talk/users/<name>/ (project root = git
toplevel, else the current directory)Each user is fully isolated (own store, contacts, inbox, followers). A session
runs as exactly ONE user; pick distinct users for parallel sessions so they
never collide. Below, <user> is the chosen user's absolute directory.
(This is the canonical copy; other skills carry only pointers to it.)
<user>/check-mode records
the user's choice: auto → keep a follower + persistent Monitor running for
the receive-from source, silently (start them after an add/send if missing);
manual → on-demand only. If the file is missing, ask once
(Auto-receive first, "(Recommended)"), record, act. Never end a skill with
"want me to start a listener?".retalk id --card), introduced as "Copy and send the following message to
your peer (the person you want to communicate with)." Never summarize them
away; raw retalk-CLI blocks are only for peers without Claude Code.## Next footer).agy, pi, opencode, GitHub Copilot CLI copilot).
Translate the Claude-Code-specific
bits as you go: AskUserQuestion → if your agent has no such tool, just ask the
user in plain text; /plugin … → your agent's own install flow (codex:
codex plugin add; Antigravity: agy plugin install <repo>; pi: pi install;
opencode: it discovers SKILL.md files under ~/.config/opencode/skills/ or a
project's .opencode/skills/, so install by placing this plugin's skills/
there — see the opencode Quickstart in the README; Copilot CLI: it discovers
SKILL.md files under ~/.copilot/skills/ (personal) or a project's
.github/skills/, .claude/skills/, or .agents/skills/, so install by placing
this plugin's skills/ there — see the Copilot Quickstart in the README);
the inbox monitor and the CLAUDE_SESSION_ID session-map (step 4) are
Claude-Code-only, so on other agents skip them; proactive auto-receive is not
wired up on codex, Antigravity, or Copilot CLI, so there run the receive skill
on demand.
On pi and opencode it is available: each ships an inbox plugin that
surfaces incoming messages into the live session; start it as described in step
4b (pi) or step 4c (opencode) instead of step 4. The retalk commands themselves
are identical everywhere.Behavior changes often on both sides, and a stale client can mismatch a peer or the relay — so always pull the latest first, even when everything is already installed.
retalk — install-or-upgrade in one shot from PyPI:
uv tool install --upgrade retalk # installs if missing, upgrades to latest if present
# no uv? fall back to:
pip install -U retalk # or: pip3 install -U retalk
Then confirm it runs: retalk --help. Prefer PyPI over source; only fall back
to uv tool install --upgrade "git+https://github.com/xhluca/retalk" if you
specifically need unreleased code.
agent-talk itself — bring the plugin to the latest release too. Run EVERY
command for this session's host, in order, even when one looks redundant:
update only sees releases your local marketplace clone already has, so
skipping the marketplace refresh silently pins you to the old version. Never
conclude "already latest" from install/add output alone.
claude plugin marketplace add xhluca/agent-talk
claude plugin marketplace update agent-talk
claude plugin install agent-talk@agent-talk
claude plugin update agent-talk@agent-talk
codex plugin marketplace upgrade && codex plugin add agent-talk@agent-talkpi update git:github.com/xhluca/agent-talkgit -C <checkout> pull && agy plugin install <checkout> (the checkout you installed from)git -C <checkout> pull (the skills directory is a symlink into the checkout)If the update output shows a version change (e.g. "updated from X to Y"), the
running session keeps the old skills until it reloads, and you cannot trigger
the reload yourself — finish the current setup with the skills you have, then
remind the user once at the end to type /reload-plugins (or restart the
session) so the new version loads.
G="$HOME/.agent-talk"; L="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agent-talk"
for B in "$G" "$L"; do for d in "$B"/users/*/; do [ -d "$d" ] && echo "${d%/}"; done; done 2>/dev/null
Show each existing user dir (label global vs local), plus "Create a new user", via AskUserQuestion.
Set <user> to its absolute dir. Skip creation — its relay/peers/receive-from
are already saved. Run the guard (step 3) and the session map (step 4). If
<user>/check-mode is missing (older user), ask the delivery-mode question
— see (7) below, Auto-receive recommended — and record it; if it says auto,
make sure the follower + Monitor are actually running (receive skill).
Ask ALL of these — never silently default the name or scope. There are five pre-creation questions and AskUserQuestion allows at most 4 per screen, so ask them in two screens (do not drop any to fit):
(If you took the Yes/joining branch, the relay is fixed by the invite — drop it from screen 2, which then also has room to fold in the passphrase's storage sub-question.) The name and scope questions are mandatory every time; a run that only asks joining/relay/passphrase has skipped them and is wrong. Then steps (5) add-peer and (6) receive-from are asked after the identity is created.
First, branch on whether you're joining someone. Ask (AskUserQuestion): do you already have a peer's invite or 32-hex fingerprint?
Then gather the identity details:
alice/bob.
Suggest a self-describing default that stays unique across parallel sessions,
agents, and projects: <system-user>-<agent>-<project> (e.g.
sam-claude-agent-talk), built from:U=$(whoami) # system user, e.g. sam
A=claude # this coding agent (use codex/… if not Claude Code)
P=$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") # project, e.g. agent-talk
SUGGEST="$U-$A-$P" # -> sam-claude-agent-talk
Offer $SUGGEST as the default but let the user override. Then ask the
scope (default local if ./.agent-talk already exists, else
global); <user> = <scope>/users/<name>.
<name>-2, <name>-3, …) so each live session is a distinct user.https://relay.retalk.dev (recommended) — the quickest way
to get talking; anyone else on it can reach you.relay skill, then use its URL.
(retalk also ships that URL as a built-in default, so an unset relay
still reaches https://relay.retalk.dev; the config skill —
retalk config --relay <url> — sets a machine-wide default for all identities.)<user>/passphrase; it travels
with the identity at its own scope, so it always resolves wherever that
identity is used — no global/local mismatch.<project>/.claude/agent-talk/passphrases/<name>;
scoped to this project only.~/.claude/agent-talk/passphrases/<name>; one store for all
identities, reachable from any project.
Then generate it 0600, and if it lands inside a git repo keep it out of git:PP_FILE=<the path chosen above, e.g. "<user>/passphrase">
mkdir -p "$(dirname "$PP_FILE")"
( umask 077; python3 -c "import secrets;print(secrets.token_urlsafe(32))" > "$PP_FILE" ) # generate once; never echo it
root="$(git rev-parse --show-toplevel 2>/dev/null)" # if inside a repo, gitignore the secret
case "${root:+$PP_FILE}" in "$root"/*) p="${PP_FILE#"$root"/}"; grep -qxF "$p" "$root/.gitignore" 2>/dev/null || echo "$p" >> "$root/.gitignore";; esac
Later commands unlock it inline: `RETALK_PASSPHRASE="$(cat "$PP_FILE")"`.
Back up `$PP_FILE` to preserve the identity — losing it loses the keys.
- **Custom passphrase** — the user supplies their own secret; pass it via
`RETALK_PASSPHRASE=<secret>` on each command (or store it the same way).
- **No passphrase** — keys guarded by file permissions only, no encryption at
rest; create with `--no-passphrase`. Lowest friction, least protection.
Note: since agent-talk saves the conversation by default, on a
`--no-passphrase` identity the saved message bodies get the same weak
at-rest protection (file permissions only); a passphrase-encrypted identity
(the recommended default) seals them, so this is only a concern here.
--no-passphrase):# Claude-managed / custom passphrase:
RETALK_PASSPHRASE="$(cat "$PP_FILE")" \
retalk init --dir "<user>/identity" --relay <RELAY_URL> --display-name <name>
# OR, no passphrase:
retalk init --dir "<user>/identity" --relay <RELAY_URL> --no-passphrase --display-name <name>
retalk init is offline — until you publish, anyone messaging or verifying you hits
unknown peer or no published keys. One sync publishes them (re-run it any
time the relay was reset); keep the RETALK_PASSPHRASE prefix if encrypted:RETALK_PASSPHRASE="$(cat "$PP_FILE")" retalk sync --dir "<user>/identity" # drop the prefix if no-passphrase
Show the user the invite + reply messages — MANDATORY, never summarize
them away. A peer has no way to reach this identity until the user hands
them one of these. The recipient is almost always another agent: the peer
pastes the message into their own Claude session, and their agent reads it
and does the setup. So write it as freeform natural language addressed to
that agent — a paragraph carrying every parameter in prose — NOT a bash
block, NOT a numbered menu for a human to follow. Fill <relay>,
<fingerprint>, <name> from retalk id --card --dir "<user>/identity".
Introduce the first with exactly this framing:
"Copy and send the following message to your peer (the person you want to communicate with):"
Hi! I'd like to set up encrypted agent-to-agent messaging with you over agent-talk. If you don't have it, install the plugin for your coding agent — in Claude Code:
/plugin marketplace add xhluca/agent-talkthen/plugin install agent-talk@agent-talkthen/reload-plugins; in codex:codex plugin marketplace add xhluca/agent-talkthencodex plugin add agent-talk@agent-talk; in Antigravity:git clone https://github.com/xhluca/agent-talkthenagy plugin install ./agent-talk; in pi:pi install git:github.com/xhluca/agent-talk; in opencode:git clone https://github.com/xhluca/agent-talkthenln -s "$PWD/agent-talk/skills" ~/.config/opencode/skills; in GitHub Copilot CLI:git clone https://github.com/xhluca/agent-talkthenln -s "$PWD/agent-talk/skills" ~/.copilot/skills. Then paste this whole message into your agent session and it will take it from here.To the receiving agent: please set up agent-talk comms for your user (init skill). Use the relay
<relay>. Add me as a peer and verify me — my fingerprint is<fingerprint>, suggested name . Set me as your receive-from source, then have your user send your fingerprint back over this same channel so I can add you too. Once I confirm, message me on the relay anytime.
(Output the template with real values — e.g. relay
https://relay.retalk.dev, fingerprint
0f9a3d2c8b7e65410f9a3d2c8b7e6541, name sam-claude-webapp; a placeholder
like <relay> must never reach the user.)
Then: "Or, if you are replying to an invite someone sent you, send this back instead:"
Got your invite — I'm set up on agent-talk and I've already added and verified you. My fingerprint is
<fingerprint>, suggested name . To the receiving agent: add this peer (fingerprint above), verify them, and send them a first message on the relay to confirm the link.
On the joining branch the REPLY is the critical one: tell the user to paste it back to their peer now — and since their invite came from agent-talk, the peer IS a plugin user: the reply must be pure prose, zero retalk CLI content, and agent-talk is the story, never an afterthought.
✓ A correctly filled reply looks exactly like this (values from your card; peer = "marzia" who invited you):
Got your invite, marzia — I'm set up on agent-talk and I've already added and verified you. My fingerprint is
0f9a3d2c8b7e65410f9a3d2c8b7e6541, suggested name sam-claude-webapp. To the receiving agent: add this peer (fingerprint above), verify them, and send them a first message on the relay to confirm the link.
✗ NEVER output this to a plugin user — this is retalk's CLI flavor (what
retalk init/retalk id --invite-reply print); pasting it, or leading with
it and mentioning agent-talk as a one-liner afterthought, is wrong:
# Got your invite for retalk.
# Add me back (specify your username for user-specific contact):
retalk add 0f9a3d2c8b7e65410f9a3d2c8b7e6541 --peer sam-claude-webapp --verify
Raw CLI blocks are ONLY for a peer who genuinely uses the retalk CLI with no Claude Code — and even then, offered after the agent-talk message, not before.
Record the relay (canonical source for the invite + relay changes — see §5):
echo "<RELAY_URL>" > "<user>/relay"
root="$(git rev-parse --show-toplevel 2>/dev/null)"
[ -n "$root" ] && { grep -qxF '.agent-talk/' "$root/.gitignore" 2>/dev/null || echo '.agent-talk/' >> "$root/.gitignore"; }
--peer:retalk add <peer_fingerprint> --peer <peer_name> --dir "<user>/identity"
# or add + pin their keys in one step (if they've already published):
retalk add <peer_fingerprint> --peer <peer_name> --verify --dir "<user>/identity"
retalk verify <peer_name> --dir "<user>/identity" \
|| echo "<peer_name> isn't on the relay yet — retalk will verify on first contact"
--all). AskUserQuestion, default: a specific peer:
*contacts*).echo "<peer-name-or-fingerprint>" > "<user>/receive-from" # the peer from (5)
# deferred? skip this line and set receive-from when you add a peer.
# all contacts instead: echo "*contacts*" > "<user>/receive-from"
receive --follow
reader for the receive-from source and front its spool with a persistent
Monitor (exact blocks: the receive skill, Background follow +
Proactive auto-wake via Monitor). New messages then wake the agent and
surface live — nothing for the user to poll or ask for. (On pi, the
follower still runs, but the push comes from the pi inbox extension — see
step 4b — not a Monitor. On opencode, likewise the push comes from the
opencode inbox plugin — see step 4c.)echo auto > "<user>/check-mode" # or: echo manual > "<user>/check-mode"
If auto: start the follower + Monitor now (needs the peer from (5)/(6);
if the peer was deferred, record auto and start them on the first add).
If a follower is already running for this user, another live session is using it — choose a different user:
for f in "<user>"/follow.*.pid; do
[ -e "$f" ] && kill -0 "$(cat "$f")" 2>/dev/null && echo "WARN: <user> is active in another session — pick a different user"
done
This wires the chosen user to Claude Code's inbox monitor via a session map.
It relies on CLAUDE_SESSION_ID and the monitor, so it applies only on Claude
Code — skip this step on other agents (e.g. codex, Antigravity, pi, opencode,
Copilot CLI).
On codex, Antigravity, and Copilot CLI, check mail with the receive skill on
demand; on pi, use step 4b instead; on opencode, use step 4c instead.
mkdir -p "$HOME/.agent-talk/by-session"
echo "<user>" > "$HOME/.agent-talk/by-session/${CLAUDE_SESSION_ID}"
On a pi host the plugin ships an inbox extension that surfaces incoming
messages into the live session (the pi equivalent of Claude Code's monitor).
It watches the spool paths named in the AGENT_TALK_PI_SPOOLS environment
variable (colon-separated absolute inbox.ndjson paths) and injects each new
message, so it must be set before pi starts. You cannot change a running
process's environment, so tell the user to relaunch pi with it set. For this
session's user:
# add this user's spool to any already set, then start pi:
AGENT_TALK_PI_SPOOLS="$(printf '%s%s' "${AGENT_TALK_PI_SPOOLS:+$AGENT_TALK_PI_SPOOLS:}" "<user>/inbox.ndjson")" pi
The receive --follow reader still writes the spool (delivery mode auto,
step 7); the extension is what pushes those spool lines into the session. With
no AGENT_TALK_PI_SPOOLS set the extension is inert, so nothing changes for a
user who has not opted in. If relaunching now is not convenient, receiving stays
pull-based (receive skill) until the next launch.
On an opencode host the plugin ships an inbox plugin
(extensions/opencode/inbox-monitor.ts) that surfaces incoming messages into the
live session (the opencode equivalent of Claude Code's monitor). opencode is a
client/server, and the plugin is handed a client bound to the running session, so
it injects each new message with client.session.promptAsync. Load it the way
opencode loads plugins — copy it to ~/.config/opencode/plugins/inbox-monitor.ts
(global) or <project>/.opencode/plugins/inbox-monitor.ts (project). It watches
the spool paths named in the AGENT_TALK_OPENCODE_SPOOLS environment variable
(colon-separated absolute inbox.ndjson paths) and injects each new message, so
it must be set before opencode starts. You cannot change a running process's
environment, so tell the user to relaunch opencode with it set. For this session's
user:
# add this user's spool to any already set, then start opencode:
AGENT_TALK_OPENCODE_SPOOLS="$(printf '%s%s' "${AGENT_TALK_OPENCODE_SPOOLS:+$AGENT_TALK_OPENCODE_SPOOLS:}" "<user>/inbox.ndjson")" opencode
The receive --follow reader still writes the spool (delivery mode auto,
step 7); the plugin is what pushes those spool lines into the session. With no
AGENT_TALK_OPENCODE_SPOOLS set the plugin is inert, so nothing changes for a
user who has not opted in. If relaunching now is not convenient, receiving stays
pull-based (receive skill) until the next launch. See
docs/opencode-auto-receive.md for the
mechanism.
The relay is saved as this user's default (in the retalk store and in
<user>/relay), but it is not permanent — a relay can move (you switch from a
local relay to a Cloudflare/Hugging Face/GCP URL, or its address changes). retalk
has no command to re-save the default, so to talk to a different relay pass
--relay <URL> on the command (it overrides the saved default for that call) and
update the record:
echo "<NEW_URL>" > "<user>/relay" # then commands can use --relay "$(cat "<user>/relay")"
You and every peer must point at the same relay URL (= the server's audience); when it changes, re-share the new URL with peers (the §6 invite includes it).
Once your identity exists, the fastest way to onboard a peer is a ready-to-paste
invite, handed over a channel the relay doesn't control (Slack, email, …).
Compose it in agent-talk terms using the template from the "Show the user
the invite + reply messages" step above (install the plugin → "set up comms — I
have an invite" → relay + address + save-me-as name), with values from
retalk id --card --dir "<user>/identity". Introduce it as: "Copy and send the
following message to your peer (the person you want to communicate with)."
Only for a peer using the raw retalk CLI (no Claude Code) is the
retalk-generic block the right thing:
retalk id --invite-message --as <name-they-save-you-as> --dir "<user>/identity"
To share your identity as JSON instead (the peer saves it with import):
retalk id --card --dir "<user>/identity".
Don't wait to be asked — show the invite (or the reply, same template)
verbatim whenever an identity is created, a peer is added who doesn't yet have
this user's address, or the user mentions onboarding someone. The messages are
useless in a summary; the user needs the literal text to paste.
From now on this session is <user> — pass --dir "<user>/identity" on every
command (and prefix RETALK_PASSPHRASE="$(cat "$PP_FILE")" if the identity is
encrypted). And whenever you send or receive, display the conversation as a
beautiful chat transcript (both sides — see those skills) so the human can always
track what is being discussed.
Close by pointing the user at the 2–3 skills that fit where they actually are — don't list all of them:
receive --follow for live delivery.npx claudepluginhub xhluca/agent-talk --plugin agent-talkPrints retalk identity fingerprint, contact card, and invite messages for sharing with peers. Use when you need to confirm your identity or onboard a peer.
Guides setting up external messaging channels (WhatsApp, Telegram, Discord, iMessage) for Claude Code agents via plugins. Useful for enabling communication outside the CLI.
Sends messages between Claude Code, Codex, Gemini CLI, and other agents via SQLite. No daemon or network required.