Email digest for any time period — today, yesterday, last N hours/days, this week, a specific date, or while-I-was-away ranges. Categorizes by urgency, surfaces unread, flags financial/security emails, filters noise. Auto-invoke when user asks about email for any time period: "what came in today", "catch me up", "any emails this week", "what did I miss", "emails from yesterday", "last 3 hours", "since Monday".
Install
npx skillscat add aashari/ai-agent-skills/mail-digest Install via the SkillsCat registry.
Mail Digest — Email Summary for Any Time Period
Period: $ARGUMENTS (default: today)
Step 1: Parse the time expression into a Unix timestamp range
Interpret $ARGUMENTS naturally. Map to START and END unix timestamps:
| Expression | START | END |
|---|---|---|
| "today" / (empty) | midnight today local | now |
| "yesterday" | midnight yesterday | midnight today |
| "last 2 hours" | now − 7200s | now |
| "last N hours" | now − N×3600s | now |
| "last N days" | now − N×86400s | now |
| "this week" | last Monday midnight | now |
| "last week" | Monday of prev week | last Sunday midnight |
| "since Monday" | most recent Monday midnight | now |
| "YYYY-MM-DD" | that date midnight | that date 23:59:59 |
| "catch me up" / "while I was away" | infer from context or ask — default last 5 days |
DB="$HOME/Library/Mail/V10/MailData/Envelope Index"
# Use SQLite date expressions directly — never compute Unix epochs manually (wrong year risk).
# "today" → >= date('now','localtime')
# "yesterday" → >= date('now','-1 day','localtime') AND < date('now','localtime')
# "last 48h" → >= datetime('now','-48 hours')
# "last 7 days" → >= date('now','-7 days','localtime')
# Apply the range in the WHERE clause below using datetime() comparisons.Step 2: Pull all messages in the window
sqlite3 "$DB" "
SELECT datetime(m.date_received,'unixepoch','localtime') as dt,
s.subject, a.address as sender, a.comment as name,
mb.url as mailbox, m.ROWID,
m.read, m.flagged, m.is_urgent, m.size,
m.automated_conversation, m.unsubscribe_type
FROM messages m
JOIN subjects s ON m.subject = s.ROWID
JOIN addresses a ON m.sender = a.ROWID
JOIN mailboxes mb ON m.mailbox = mb.ROWID
WHERE datetime(m.date_received,'unixepoch','localtime') >= date('now','localtime')
-- adjust the expression above based on $ARGUMENTS
AND m.deleted = 0
AND mb.url NOT LIKE '%Spam%'
AND mb.url NOT LIKE '%Trash%'
AND mb.url NOT LIKE '%Junk%'
AND mb.url NOT LIKE '%Draft%'
AND mb.url NOT LIKE '%Sent%'
ORDER BY m.is_urgent DESC, m.date_received DESC;" 2>/dev/nullStep 3: Categorize
Tier 1 — Needs Attention (read these):
is_urgent = 1flagged = 1read = 0ANDautomated_conversation != 2ANDunsubscribe_type = 0(real person, unread)- Subject contains: urgent, action required, ASAP, deadline, critical, review, approve, response needed
Tier 2 — FYI (skim):
automated_conversation = 1(transactional — Jira, Slack, bank receipts)- Reports, summaries, meeting notes, calendar invites
- Security alerts (login, 2FA, password) — even if automated
- Financial: receipts, payment confirmations, invoices
Tier 3 — Noise (count only, don't enumerate):
automated_conversation = 2— bulk automated emailsunsubscribe_type > 0— newsletter/mailing list emails- Monitoring subjects: PROBLEM:, OK:, ALARM:, CRIT:
- Break down by type: monitoring alerts, newsletters, marketing
For Tier 3, just report: "X automated emails (Y monitoring, Z CI/CD, W marketing)"
Step 4: Read bodies for Tier 1 items
For Tier 1 emails, get body preview. Check summaries table first (fast, no file I/O):
sqlite3 "$DB" "
SELECT m.ROWID, su.summary
FROM messages m
JOIN summaries su ON m.summary = su.ROWID
WHERE m.ROWID IN (<ROWID1>,<ROWID2>,...)" 2>/dev/nullFor any ROWIDs without a summary entry, fall back to emlx parsing:
python3 ~/.claude/skills/_mail-shared/parser.py <ROWID1> <ROWID2> ...Pull the first ~200 chars of meaningful body content as a preview for each.
Step 5: Surface special categories
Always call out explicitly if found in the window:
- Financial: subjects containing payment, receipt, invoice, transfer, transaction, billing → note count and total if parseable
- Security: login alerts, 2FA changes, password resets → flag every one
- Meetings: calendar invites or reschedules
- Unread from humans: these are the most likely to need action
Output Format
Email Digest — [RESOLVED DATE RANGE]Total: X | Unread: Y | Flagged: Z | Accounts: N
Needs Attention (X)
| Time | From | Subject | Preview |
|---|
FYI (X)
| Time | From | Subject |
|---|
Financial (X emails)
- Brief list of transactions if detectable from subjects
Security (X events)
- Brief list
Noise filtered: X total (breakdown by type)
If the window has zero emails: say so and suggest a wider range.
If the window has >200 emails: summarize by sender domain and ask if they want to drill into a category.
Offer at the end: "Want me to read any of these, or dive into a specific category?"