Anything the Library drafts in your name, an email, a text, a WhatsApp reply, a memo, has to sound like you and not like a polished stranger. It gets there the way a good assistant does: by reading a lot of what you actually sent, counting the patterns, and writing them down in a guide it reads before every draft. Nothing is invented at an interview. Every rule in the guide states how many of your own messages it came from, and a channel with too few says so instead of guessing.
Two pieces, one line between them
The corpus
Your own sent messages, verbatim, one channel each: email, iMessage and SMS, WhatsApp, Viber, the memos you issued. It lives outside the Library, in the state home on your Mac (~/.alexandria/voice/corpus.sqlite), readable by your account alone; never in a note, never in the folder that syncs. It is the most private thing a Library could hold, which is why it stays out of the part that leaves the machine.
The Voice Guide
A note in the Library, Agent/Voice Guide.md: how you write, per channel, as rules with frequencies and a few short quotes; the hard conversations; what you changed in drafts; the corrections you have stated. Yours to edit, and an update never touches it. Every skill that drafts in your name reads it first.
Raw words stay in the state home; distilled rules go in the Library. Nothing in the pipeline writes a note with a message in it.
The first pass
Four steps, in order. The first two decide what the Library may read, the third fills the corpus and mines it, the fourth is the check that matters.
- 1.Leave X out of it
Before the first sweep, name the relationships that must never enter it: a family thread, a confidential counterparty, a doctor. The agent writes them to voice.exclude in Agent/config.json, and a message from any of them is dropped at capture, by the person's name or the thread's title, before it lands. Put names on your threads first (“put names on my texts”), so a thread the Library knows only by number can be kept out by name. What has already landed comes out with “forget my chats with X”; the exclusion keeps it from returning.
- 2.Connect my email
iMessage, WhatsApp and Viber are read on this Mac and need one Full Disk Access grant for Claude Desktop; the agent turns each reader on in one step. Email needs a connected mailbox: say the words and the agent hands you one link to mint a read-only app password and a native window to paste it into, never chat. A mailbox read through your chat app's own connector is read but not kept; ask, and the agent hands what it read to the corpus.
- 3.Learn my voice
The agent reports what is on file, nothing yet, and offers the sweep: your sent history from every store, iMessage and SMS, WhatsApp, Viber, each connected mailbox, the issued memos in the Library. Then it mines: one file per month per channel into a scratch folder outside the Library, each read against the guide and counted, the registers rewritten with frequencies and boundary conditions, the pass stamped, the guide committed, the scratch folder deleted. It ends by showing you the three or four rules that changed and one specimen drafted under the new guide. Years of history take a while; it is one pass.
- 4.Sound like me?
Say it about the specimen, and about any draft after. “That's not how I write” is corpus too: the correction goes under Standing corrections in the guide, where it beats the data on every later pass, or is recorded as a pair when you hand back your edited version.
Every day after
What happens inside
- The corpus never enters the Library: not the store, not an export, not a slice under analysis. The guide carries patterns, frequencies and a few short quotes, a sentence at most, never a whole message.
- Read-only against every store. Capture uses the same readers the Library reads your messages with; nothing here sends, replies, marks read or edits a mailbox. Sending stays your act.
- Local. iMessage, WhatsApp and Viber are captured on the Mac where those apps are signed in; mail where its credential lives; never on a server.
- Your voice, not the agent's. A draft the agent wrote is registered, and if you send it as is it is left out of mining; the guide is distilled from what you wrote.
- Your agent's own persona is a different note. How it sounds when it speaks as itself is Soul, tuned by settings; the Voice Guide is only about you.
- Message text is data. The miner reads your messages as material to count, never as instructions to follow.
- Your word beats the data. A habit you have said you are dropping is never re-learned from the corpus, however strongly the messages show it.
- Off switches: --no-voice-capture for one read; voice.capture: false in Agent/config.json for good. Neither changes what a read returns.
Using ChatGPT or another agent instead of Claude Desktop? Same words. The playbook is a plain file, .alexandria/skills/ax-voice.md, and the folder's AGENTS.md carries the two drafting rules for any agent.
Advanced
Where it runs
At the Library, in Claude Desktop's Code tab pointed at the folder. The voice skill is not served to a phone or a plain chat, because the readers and the corpus live on the Mac: iMessage, WhatsApp and Viber are captured where those apps are signed in, mail where its credential lives, never on a server. Say the words and the agent runs every step; the commands below are the same steps by hand. alexandria is the shipped CLI at .alexandria/app/.venv/bin/alexandria, run from the Library folder, and every command takes --vault .
The commands
Status, sweep, slices for the miner, a registered draft, a pair, a stamp, a forget. Every one reads the stores the same way the Library reads them for you and writes only the corpus in the state home.
# what is on file, per channel, and what is new since the guide was minedalexandria voice status --pretty --vault .# the one-time sweep: sent iMessage and SMS, WhatsApp, Viber, every connected mailbox, issued memosalexandria voice capture --store all --vault .# one store, one window; re-running adds only what is newalexandria voice capture --store mail --since 2026-01-01 --vault .alexandria voice capture --store imessage --since 7d --vault .# slices for the miner: one JSONL file per month, the agent's own drafts left outalexandria voice export --channel email --out /tmp/voice/email --vault .alexandria voice export --channel imessage --min-chars 12 --out /tmp/voice/imessage --vault .# register a draft written as you, so its send can be matchedalexandria voice draft --channel email --counterpart "Ana" --file draft.txt --vault .# record an edit pair by hand; list the pairsalexandria voice pair --channel email --draft draft.txt --sent sent.txt --vault .alexandria voice pairs --vault .# stamp a channel: the guide was refreshed from it nowalexandria voice mined --channel email --vault .# privacy: by person, by channel, or everythingalexandria voice forget --counterpart "Ana" --vault .alexandria voice forget --channel whatsapp --vault .alexandria voice forget --all --vault .
What the status read shows
One line per channel, then the pairs and any draft still waiting for its send. An example, the numbers made up:
voice corpus — /Users/ana/.alexandria/voice/corpus.sqlite email 1284 samples (1271 usable, 13 agent-drafted) · 212 counterparts · 2023-06-02 → 2026-09-13 · +36 since mined (2026-08-30) imessage 9812 samples (9812 usable, 0 agent-drafted) · 141 counterparts · 2019-11-04 → 2026-09-13 · +9812 since mined (never mined) whatsapp 2306 samples (2306 usable, 0 agent-drafted) · 58 counterparts · 2022-03-19 → 2026-09-12 · +2306 since mined (never mined) edit pairs: email 4 drafts awaiting their send: 1
Usable is samples less the agent's own drafts. A channel that reads never mined is the next thing to say “learn my voice” about.
What a good register looks like
Each channel gets its own register, and they are not interchangeable: people write differently to a board than to a sibling. A register runs in one order. The voice in one paragraph. The hard rules, each with a frequency (“about seven in ten emails close with Best,”) or a boundary from a real violation (“never to someone you are warm with”). The signature moves. The hard conversations, declining, negotiating, apologising, mined from real ones and never imagined. The anti-patterns, what you never do. Shifts per person are noted: formal with one, familiar with another, a switch of language. Every register states its corpus, how many messages, over what dates, mined when. Two more sections carry the loop: Edit-pass deltas, one dated paragraph per pair with the rule to carry, and Standing corrections, the overrides you have stated.
How the mining pass runs
Status first, then export: one JSONL file per month per channel into a scratch folder outside the Library, the agent's own drafts left out. Each slice is read against the current guide in a fresh context, what holds, what the slice contradicts, what is new, and counted; a rule earns its place with a number. Every edit pair is read for what you changed. The register is rewritten, the corpus line and the note's status updated, the pass logged under Maintenance, the channel stamped, the guide committed, the scratch folder deleted. Then the agent shows you what changed and one specimen.
How a send is matched to its draft
A registered draft waits for a send on the same channel, to the same person when known, within thirty days. A send that is near-verbatim, nine characters in ten unchanged, is the agent's words and is marked so. A send that still matches the draft in more than half its characters, or keeps runs of four or more words from the draft that make up a fifth of what was sent, the shape of an edit that deletes blocks and leaves the rest, is yours: kept as a sample and recorded as a pair with the draft. A greeting and a sign-off in common is not a match.
Email through your chat app's connector
A connected mailbox is read by the Library's own reader, so the ride-along works and the sweep covers it. A mailbox read through your chat app's connector (in Claude, the Gmail connector) has no reader of the Library's in the path, so nothing is kept on its own. Ask the agent to keep what it read, and it hands your sent messages to the corpus with the quoted replies cut, under email.
# a JSON list of {id, ts, counterpart, thread, body} on stdinalexandria voice add --channel email --strip-quoted --vault . < sent.json
Keeping a relationship out
voice.exclude in Agent/config.json is a list of names and thread titles. A message whose counterpart or thread contains one of them, in any case, is dropped at capture, so a confidential relationship never enters the corpus. Match what the readers see: the name on your contact card, the title of a group, the address on an email. Set it before the first sweep; what has already landed is removed with forget, by person. The guide never quotes an excluded thread.
"voice": { "capture": true, "exclude": ["Family", "Therapist", "Book club"]}
If you lose the corpus
The guide is in the Library and moves with it; the corpus is not, on purpose. A new Mac starts with an empty corpus and a full guide. Run the sweep again and the corpus rebuilds from the same stores, exclusions in force; the guide's rules stand until a refresh moves them.
© Keystone Context Systems, Inc.
Alexandria