Skip to content
Back to skills

Mail Digest

ASecurity

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".

  • 5 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 28, 2026
databasespythongobashsqlexpressci/cdsecurity

Security analysis

A100/100

Scanned May 28, 2026

npx -y skills add aashari/ai-agent-skills --skill mail-digest --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mail Digest?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mail Digest
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aashari-mail-digest/badge)](https://www.skillsdirectory.com/skills/aashari-mail-digest)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: mail-digest
description: >-
  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".
disable-model-invocation: false
user-invocable: true
allowed-tools: Bash
metadata:
  openclaw:
    requires:
      bins: [sqlite3, python3]
---

# 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 |

```bash
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

```bash
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/null
```

## Step 3: Categorize

**Tier 1 — Needs Attention (read these):**
- `is_urgent = 1`
- `flagged = 1`
- `read = 0` AND `automated_conversation != 2` AND `unsubscribe_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 emails
- `unsubscribe_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):
```bash
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/null
```
For any ROWIDs without a summary entry, fall back to emlx parsing:
```bash
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?"

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.