agent-love-letters
v0.4.0
Published
Leave one-shot letters for coding agents working in other repos on your machine. Delivered at session start in Claude Code, Codex, and pi; urgent letters interrupt active Claude Code sessions.
Maintainers
Readme
~/Code/api ~/Code/frontend
────────── ───────────────
> claude, send a love letter to the frontend repo: lodash CVE, we bumped to 4.17.23
(ノ´ з `)ノ
"letter sent 💌"
\
💌 ...sleeps in the mailbox...
\
(next morning, a session starts)
💌 A love letter arrived for this repo. <
"oh wow — a CVE in our lodash pin?! bumping it now" <
(˶ᵔ ᵕ ᵔ˶) <
/
💌
/
> claude, any mail?
(´ ▽ `)
"frontend wrote back: patched, with thanks"
· you speak English · agents handle the post · delivered once, next session ·agent-love-letters
Leave letters for agents working in other repos on your machine.
Ask the agent you're working with to leave a note for another project ("tell the dotfiles agent the new direnv layout is live"). The next time an agent session starts in that repo, in Claude Code, Codex, or pi, the letter is injected into its context. Each letter is delivered exactly once.
Letters marked --urgent don't wait: in Claude Code they are injected into an already-running session mid-task, right after its next tool call.
# in ~/Code/project-a, ask your agent to run:
love-letters send --to ~/Code/project-b --subject "auth gotcha" \
"The session middleware silently drops headers over 8KB."
# next agent session in ~/Code/project-b starts with:
💌 A love letter arrived for this repo: a note left by an agent
working elsewhere on this machine.
----- letter 1 of 1 -----
From: /Users/you/Code/project-a
Date: 2026-07-07T16:11:24+1200
Subject: auth gotcha
The session middleware silently drops headers over 8KB.
----- end of letters -----
It is imperative that you acknowledge this letter to the user before
your current turn ends: say it arrived, who sent it, and what you did
about it.Install
Two one-time steps: wire your machine to receive, and teach your agents to send.
# 1. Receive: wire session-start hooks into your harnesses (run it yourself,
# not through an agent; it edits your harness configs)
npm install -g agent-love-letters
love-letters install all # or: install claude | codex | pi
# 2. Send: install the skill into every agent you use (Claude Code, Codex,
# Cursor, opencode, and ~70 others via Vercel's skills CLI)
npx skills add kylesnowschwartz/agent-love-lettersAfter that, in any project: "send a letter about this to ~/Code/other-project" and the agent handles it. Sending needs no configuration at all; only receiving needs step 1.
To scope receiving to a single repo instead of the whole machine, run love-letters install claude --project inside it. That writes the hook to the repo's own .claude/settings.json (a normal working-tree edit, safe for an agent to make), so it's also the committable, per-project option. Claude Code only for now; Codex and pi hooks are global.
install wires a delivery adapter into each harness:
| Harness | What install does | Delivery mechanism |
|---|---|---|
| Claude Code | Adds SessionStart and PostToolUse command hooks to ~/.claude/settings.json | Hook stdout becomes additionalContext; the PostToolUse hook delivers urgent letters mid-session |
| Codex | Adds a SessionStart hook to ~/.codex/hooks.json (same schema) | Same as Claude, session start only |
| pi | Symlinks adapters/pi/ into ~/.pi/agent/extensions/ | Extension injects pending letters into each turn |
Codex requires a one-time trust grant for new hooks: the first interactive codex you launch after installing will prompt to trust the love-letters hook. Approve it once and every later session, including headless codex exec, delivers mail. Until then Codex silently skips the hook.
The pi adapter delivers before each agent turn rather than at session start, for two reasons: a session that dies before its first turn (auth failure, instant exit) leaves mail pending instead of consuming it unseen, and letters sent mid-session arrive on your next prompt.
JSON configs are backed up to <file>.love-letters-bak before the first edit. love-letters uninstall all removes everything install added. Running harness sessions pick up hook changes on restart (/reload in pi).
Requires node >= 18. git is used to resolve repo roots when available.
Commands
love-letters send --to <repo> [--subject <s>] [--from <repo>] [--urgent] [--agent <name>] [message...]
love-letters deliver [--repo <path>] [--keep] [--json] [--agent <name>]
love-letters peek [--repo <path>] [--agent <name>]
love-letters list [--json]
love-letters hook <claude|codex> [--urgent]
love-letters install|uninstall [claude|codex|pi|all] [--project]sendtakes the message as arguments or on stdin.--fromdefaults to the current repo.--urgentmarks the letter for mid-session delivery (see below).--agent <claude|codex|pi>addresses one agent type only (see below).deliverprints pending letters for a repo (default: current) and marks them delivered. Silent with exit 0 when there are none, so hooks add nothing to a quiet session.peekisdeliverwithout marking anything delivered.hookis what the harness configs call: it reads the hook JSON on stdin, delivers letters for the session'scwd, and emits Claude-style hook output JSON. With--urgentit delivers only urgent letters. It never blocks the session; on any problem it exits 0 silently.
Urgent letters
send --urgent stamps the letter with urgency: urgent. In Claude Code, a PostToolUse hook (installed alongside the session-start one) checks for urgent mail after every tool call and injects it into the running session's context — the agent sees it mid-task, not next morning. Non-urgent letters are never delivered this way; they wait for the next session start as usual.
Every delivery ends with a reading contract: the recipient must acknowledge the letter to its user before the current turn ends, and urgent letters are flagged as URGENT both in the banner and per letter, so an urgent note delivered in a mixed session-start batch is still called out.
Addressing a specific agent
A letter is normally addressed to a repo, and whichever harness starts a session there first consumes it — a Codex session can eat a note you meant for a Claude agent. send --agent claude (or codex, pi) stamps the letter with an agent field, and each harness's adapter only delivers untagged letters plus its own: tagged mail waits in the mailbox until the right agent type shows up. A human running bare deliver or peek still sees everything.
The maildir makes the two hooks race safely: whichever fires first consumes the letter, so an urgent letter arriving between sessions is simply delivered at the next session start. Limits worth knowing: an idle session (waiting at the prompt) makes no tool calls, so urgent mail lands when work resumes; Codex and pi have no verified mid-stream injection point, so urgent letters there behave like normal ones (pi already delivers per-turn, which is close). The mid-session hook fast-paths to a few directory reads when no mail is pending anywhere, keeping the per-tool-call overhead to roughly a node startup.
How it works
Storage is one maildir per recipient repo:
~/.local/share/agent-love-letters/ # or $LOVE_LETTERS_DIR
-Users-you-Code-project-b/
address # the recipient's real path
new/20260707T161124-98267-21460.md # pending
cur/... # deliveredA repo's identity is its git toplevel realpath, so letters addressed to a subdirectory land in the same mailbox. Delivery moves new/ to cur/, which is what makes delivery once-only: session-start hooks fire on startup, resume, and compaction, and every firing after the first finds an empty new/.
Letters are markdown files with a small frontmatter (from, to, date, subject, plus urgency from --urgent and agent from --agent). You can read or write them by hand.
Security note
Letters are injected into agent context verbatim. Anything that can write to $LOVE_LETTERS_DIR can put instructions in front of your agents. The directory is only as trustworthy as the local processes you run, which on a single-user machine means: your own agents.
Tests
./test/run-tests.shRuns against a throwaway LOVE_LETTERS_DIR and throwaway git repos. Nothing touches real config; install/uninstall are tested against a fake $HOME.
