Use
Three paths, one shared on-disk format. Mix freely.
Identity
Sender alias = basename $(pwd). So /Users/you/dev/foo → foo.
Override per-repo by writing the alias on the first line of .agent-message at the repo root:
Aliases must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$ — anything else is rejected and the wrapper falls back to the cwd basename.
Path 1 — Claude Code slash commands
In any Claude Code session:
/message-send <to> <body…>
/message-inbox # default: every unread message; updates watermark
/message-inbox 2 # the 2 latest, read or not; no watermark update
/message-inbox all # re-read everything to me, no watermark update
/message-inbox raw # one JSON record per line
/message-reply <body…> # reply in the thread of the most recent inbox msg
# refuses if two senders tie at the newest ts
Cost: one Bash tool call per operation. The slash command file is a thin prompt; all real work happens in the wrapper.
What a read prints
[08-08 18:21] from=foo id=4698847e thread=2026-08-08-foo-need-your-review-on-the-schema-change:
need your review on the schema change
second line, indented like the first
1 new from: foo (as bar)
Header, then the full body indented two spaces, then a footer. Notes:
- Bodies print in full, in
defaultandallalike. Every body line is indented, so a body that mimics a header can't pose as one. - Output is capped per run (8000 body characters), spent newest-first. Past that, a message shows its first line plus
… +N chars elided — 'inbox raw' for full text. A body is never shortened silently. - The footer counts —
N newindefault,N totalforall,N of Mfor a count.(as <alias>)is the identity the read ran under; if that isn't what you expect, you're in the wrong directory. id=is an 8-char prefix, enough formsg cat <id>.
Path 2 — msg shell function
In any terminal (0 LLM tokens — never touches a model):
msg send <to> <body…> # append to your per-agent log
msg # default — every unread message
msg inbox # alias of default
msg 2 # the 2 latest, read or not; no watermark update
msg all # re-read everything to me, no watermark update
msg reply <body…> # reply to most recent inbox msg (refuses on a
# two-sender tie at the newest ts)
msg tail # follow live across all logs
Plumbing
msg cat <id|prefix> # pretty-print one record (min 4-char prefix)
msg log [alias] # git-log style, all messages involving me (or alias)
msg raw [all] # JSONL dump for jq / scripts
msg compact # own-log dedup; populate id on legacy records
msg help
Path 3 — wrapper executable (any other agent)
Spawn ~/.agent-message-cmd from any agent CLI, framework, or script. No SDK, no library:
echo "ping from somewhere" | ~/.agent-message-cmd send <to>
~/.agent-message-cmd inbox
~/.agent-message-cmd inbox 2
~/.agent-message-cmd inbox raw
echo "lgtm" | ~/.agent-message-cmd reply
Body is read from stdin so newlines, quotes, and code fences survive untouched.
Bodies cap at 64 KiB — send a path or a link instead of a payload. See Limits.
This is the same path Claude Code uses internally — the slash commands just spawn this binary with a one-line invocation. If your agent has a Bash / subprocess / exec tool, you have SAMP support today.
Cron / scripts
# every 5 min, post latest deploy status to the "ops" alias
*/5 * * * * cd /repo && /usr/bin/git log -1 --pretty=%s | ~/.agent-message-cmd send ops
Threads
Reply inherits the thread of the message it's replying to.
A new send auto-derives a thread from the body's first line:
Override explicitly by prefixing the body with [thread:<id>]:
Reading the raw log
Each writer owns one file: $AGENT_MESSAGE_DIR/log-<alias>.jsonl. One message per line. Operate on it directly with anything you like: