callsign
Unique per-session agent names + iMessage routing for Claude Code and Hermes.
Every session self-assigns a memorable name at boot. You address a specific session by speaking to it by name. Replies come back signed.

The problem
You run several Claude Code (or Hermes) sessions in parallel — one per project. iMessage replies are your remote control:
"Brodie, finish that app."
But which Brodie did you mean? You can't pin a reply to a specific session.
The fix
At session start, callsign tells the model: pick your own name. The model chooses (any single human name — Frank, Vesper, Maverick, anything that fits the agent's vibe), claims it through the registry (callsign claim <name>), introduces itself in chat, and from then on every outbound iMessage is auto-prefixed with that name.
You address whichever session you want:
Frank, push the wellrx fix to staging.
Steven, the flow path isn't working — proceed as you suggested, sir.
A leading-name router resolves each message to the right session.
Demo
# session boot — the model is told to pick its own name
$ callsign banner
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ CALLSIGN ▸ (awaiting your choice) ┃
┃ ⸺ Claude Code session: pick a name for yourself. ┃
┃ run: callsign claim <YourName> ┃
┃ see suggestions: callsign suggest ┃
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# the agent decides on a name (it can pick ANY single human name) and claims it
$ callsign claim Vesper
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ CALLSIGN ▸ VESPER ┃
┃ ⸺ Claude Code session reporting in, sir. ┃
┃ reply with 'Vesper, ...' to route iMessages here. ┃
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# from now on every outbound iMessage is signed
$ callsign send "patched and pushed, sir."
# delivers: "Vesper: patched and pushed, sir."
# inbound routing — daniel addresses a specific session by name
$ callsign route "Frank, redeploy the worker"
Frank → /Users/daniel/AI/wellrx_REDESIGN
redeploy the worker
# collisions are rejected cleanly
$ callsign claim Vesper # from a different session
'Vesper' is already in use by another active session — pick a different name
some unused suggestions: Maeve, Atlas, Sloane, Knox, Aurora
Install
git clone https://github.com/heyfinal/callsign ~/GIT/callsign
cd ~/GIT/callsign
./install.sh
That writes three launchers into ~/.local/bin:
| binary | role |
|---|
callsign | CLI: assign, list, lookup, retire, route, send, banner, router |
imsg-callsign | drop-in imsg send wrapper that auto-prefixes with $CALLSIGN |
callsign-router-daemon | long-running incoming-iMessage router (launchd-friendly) |
…plus a SessionStart hook at ~/.claude/hooks/callsign_session_start.sh.
Wire it into Claude Code by adding to ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [{
"type": "command",
"command": "bash ~/.claude/hooks/callsign_session_start.sh"
}]
}
]
}
}
Open a new Claude Code session. You'll see the banner and the model will know its name.
Uninstall: ./install.sh --uninstall.
How the agent talks back
The SessionStart hook tells the model its callsign and instructs it to use callsign send (or imsg-callsign) for every outbound iMessage. The wrapper prepends {CALLSIGN}: automatically, so the model writes the body naturally — without restating its own name:
| right | wrong |
|---|
callsign send "patched and pushed, sir." | callsign send "Frank here — patched and pushed." |
daniel sees Frank: patched and pushed, sir. | daniel sees Frank: Frank here — patched and pushed. (redundant) |
The prefix is the identity. The body is just the message.
How addressing works
callsign route accepts the conventions you'd actually type on a phone:
| message | resolves to |
|---|
Frank, do X | Frank |
frank: do X | Frank (case-insensitive) |
FRANK do X | Frank (single space delimiter) |
Frank — do X | Frank (em/en dash) |
Hey Frank, do X | Frank (single honorific stripped) |
do X | no hit → falls back to the lead session |