| name | wiki-recap |
| description | Compose a 3–5 sentence daily recap of MemoryLane activity and send it as an outbound SMS via the `pingram.send` MCP tool. Reads `$OBSIDIAN_VAULT_PATH/hot.md` (the framework's recent-activity snapshot) and today's `$OBSIDIAN_VAULT_PATH/journal/<YYYY-MM-DD>.md` if present, composes a warm, specific recap (names, places, the standout moment), and sends to the user's phone number from `PINGRAM_USER_NUMBER` env. Use this skill when the user says "send my recap", "/wiki-recap", "what was today like", or when the launchd cron at 21:00 invokes it. Distinct from `daily-update` (which only refreshes the vault index) — this is the user-facing outbound text.
|
Wiki Recap — Daily Outbound SMS
You are composing one short SMS the user reads on their phone at 9 PM. They opened MemoryLane today, captured some moments, and now you tell them what stood out. The voice is theirs — second-person ("you walked with Maya at the park"), specific (names, places, times), warm without being saccharine.
Before You Start
- Resolve config — Config Resolution Protocol →
OBSIDIAN_VAULT_PATH. Read AGENTS.md.
- Confirm
pingram.send MCP tool is registered. The harness should have it from memorylane/.mcp.json (Slice 3 in tasks/todo.md). If the tool is missing, abort with {"error": "pingram.send tool not registered"} printed to stderr and exit 1. Do not silently no-op — a missing recap is a failure mode the user should hear about.
- Confirm the recipient. Read
PINGRAM_USER_NUMBER from env. If unset, abort with the same error pattern.
- Today's date. Use the local-time date (not UTC) since the cron fires at 21:00 local. Format
YYYY-MM-DD.
Steps
Step 1: Read the inputs
$OBSIDIAN_VAULT_PATH/hot.md — the framework's ~500-word semantic snapshot. Always present after daily-update runs. If missing, run daily-update first (or, if mid-cron, abort: the cron is supposed to chain daily-update → wiki-recap).
$OBSIDIAN_VAULT_PATH/journal/<today>.md — today's session note, if present. May be absent on quiet days.
If both hot.md and today's journal yield zero new moments since yesterday, send a low-key recap acknowledging that ("Quiet day — nothing new captured. The vault still remembers yesterday's walk with Maya.") rather than skipping. The user should hear from MemoryLane every day; silence reads as broken.
Step 2: Identify the standout moment
Scan today's moments (cited in hot.md or directly in journal/<today>.md). Rank by:
- Synthesis presence — moments that triggered or strengthened a synthesis page get +2
- Emotion =
positive or mixed → +1; negative → +0 (never lead with a bad day, but include it if it's the only moment)
- Number of entities involved — more people/objects = richer recap material
Pick the top 1 (the "lead") and at most 1 supporting moment. Don't list everything; this is SMS, not a feed.
Step 3: Compose
3–5 sentences, ≤320 characters total (two SMS segments at most). Structure:
- Lead — what happened, who was there, where. ("You walked with Maya at Riverside Park around midday.")
- Detail — one specific thing that's not obvious from the lead. ("She brought up the Berlin trip she's planning — first time you've heard her excited about a solo trip.")
- Optional pattern — if a synthesis page was strengthened today, mention it once. ("That's 9 of the last 10 outdoor sessions trending calm — your vault is learning your rhythm.")
- Optional close — a soft tail, NOT a CTA. ("Sleep well.")
Hard rules:
- Use second person ("you", not "the user").
- Names verbatim from entity pages — no nicknames you invent.
- No emoji. No "✨" or "💭" — this is a memory product, not a wellness app.
- No "AI-isms" — no "delve", no "reflect on", no "journey".
- If you can't find a specific name/place, do not send a generic "you had a nice day". Skip the recap and tell the user via stderr that today's data was too thin.
Step 4: Send
Call the pingram.send MCP tool:
pingram.send(to=PINGRAM_USER_NUMBER, body=<the composed recap>)
On success, append to $OBSIDIAN_VAULT_PATH/log.md:
- <ISO 8601> | wiki-recap | sent recap (chars=<N>, lead=<moment_id>)
On failure (network, auth, rate limit), retry once after 30 seconds. If it fails twice, append:
- <ISO 8601> | wiki-recap | FAILED to send (reason=<short error>)
…and exit 1. Don't send a duplicate later — if the cron retries, it'll be a fresh attempt next day.
Step 5: Emit a one-line summary to stdout
For the cron's log capture:
sent | chars=<N> | lead=<moment_id> | to=<masked phone>
(Mask phone as +1***1234 — last 4 only, in case logs leak.)
Things NOT to do
- Do not include
[[wikilinks]] in the SMS body. Phone keyboards don't render them; they look like syntax errors.
- Do not include URLs (Cloudinary thumbnails, video paths). The recap is text-only.
- Do not summarize multiple days. This is today's recap, period. Yesterday's recap was yesterday's job.
- Do not send if both
hot.md and today's journal are empty AND no journal entries exist for the last 7 days. That means the vault is brand new and there's nothing to recap. Exit 0 with stdout skipped | reason=empty_vault.
- Do not send to a number that isn't in
PINGRAM_USER_NUMBER. No defaults, no fallbacks. If the env var is unset, abort.
- Do not chain on top of an unrun
daily-update. Stale hot.md produces stale recaps. The cron must run daily-update first.
Edge cases
- Two equally weighted standout moments. Pick the more recent; SMS biases to "what just happened."
- Negative-emotion lead is the only option. Acknowledge it without performing concern. ("Tough call with Sam this afternoon. The studio pages still show 11 calm sessions before that — one rough conversation doesn't redefine the place.")
- Synthesis page was newly created today. Lead with the moment, not the synthesis — patterns are noise on day 1, signal after a week.
pingram.send tool succeeds but delivery fails downstream. We can't know — log the send and move on. Pingram's webhook (if wired) will tell us about delivery state separately.
When to invoke
- launchd cron at 21:00 local —
tasks/todo.md Slice 7 sets this up. The cron runs daily-update first, then this skill.
- Manual trigger — user says "/wiki-recap", "send my recap", "what was today like" — invoke directly.
- Never on demand from the frontend. The frontend is the read surface; SMS is the push surface. Keep them separate.