Skip to content
Back to skills

Email Triage

ASecurity

Triage Joel's email inboxes via the joelclaw email CLI. Scan, categorize, archive noise, surface actionable items, and draft replies. Use when: 'check my email', 'scan inbox', 'triage email', 'what needs a reply', 'clean up inbox', 'archive junk', 'email summary', 'anything important in email', or any request involving email inbox review or cleanup.

  • 64 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 28, 2026
content-marketingtypescriptgobashgitapisecurity

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned May 28, 2026

npx -y skills add joelhooks/joelclaw --skill email-triage --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Email Triage?

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

Security grade badge for Email Triage
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/joelhooks-email-triage/badge)](https://www.skillsdirectory.com/skills/joelhooks-email-triage)

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: email-triage
displayName: Email Triage
description: "Triage Joel's email inboxes via the joelclaw email CLI. Scan, categorize, archive noise, surface actionable items, and draft replies. Use when: 'check my email', 'scan inbox', 'triage email', 'what needs a reply', 'clean up inbox', 'archive junk', 'email summary', 'anything important in email', or any request involving email inbox review or cleanup."
version: 2.1.0
author: Joel Hooks
tags: [joelclaw, email, triage, inbox, front]
---

# Email Triage

Scan Joel's email inboxes (Front), triage conversations by importance, archive noise, and surface items needing attention. All operations use the `joelclaw email` CLI.

## Canonical Email Interface

- **Front is canonical** for all email triage in joelclaw.
- **Never use `gog gmail` for triage** — `bricks@aihero.dev`, VIP threads, and all inboxes are in Front.
- Use `gog` for Google Workspace operations (Calendar, Drive, Docs, etc.), not email triage.

## Front Search API Reference

The search endpoint is `GET /conversations/search/{url_encoded_query}`.
Results sorted by last activity date (not configurable).

### Valid `is:` statuses
`open`, `archived`, `assigned`, `unassigned`, `snoozed`, `trashed`, `unreplied`, `waiting`, `resolved`

**There is NO `is:unread`.** Use `is:unreplied` for conversations where the last message was inbound.

Status conflicts (cannot combine):
- `archived` ↔ `open` ↔ `trashed` ↔ `snoozed` (mutually exclusive)
- `assigned` ↔ `unassigned` (mutually exclusive)

### Date filters — UNIX TIMESTAMPS ONLY
- `before:<unix_seconds>` — messages/comments created before this timestamp
- `after:<unix_seconds>` — messages/comments created after this timestamp
- `during:<unix_seconds>` — messages/comments on same day as timestamp

**Front does NOT accept `before:YYYY-MM-DD`** — always convert to unix seconds.
The CLI `--before` and `--after` flags accept `YYYY-MM-DD` and convert automatically.

### Recipient filters
- `from:<handle>` — sender (email, social handle)
- `to:<handle>` — recipient (includes to/cc/bcc)
- `cc:<handle>`, `bcc:<handle>` — specific fields
- `recipient:<handle>` — any role (from, to, cc, bcc)
- Multiple same-type filters use **OR** logic: `from:a@x.com from:b@x.com` → either
- Different filter types use **AND** logic: `from:a@x.com to:b@x.com` → both

### Entity filters (use IDs, not names)
- `inbox:<inbox_id>` — e.g. `inbox:inb_41w25`
- `tag:<tag_id>` — e.g. `tag:tag_13o8r1`
- `assignee:<teammate_id>` — e.g. `assignee:tea_hjx3`
- `participant:<teammate_id>`, `author:<teammate_id>`, `mention:<teammate_id>`, `commenter:<teammate_id>`
- `contact:<contact_id>`, `link:<link_id>`
- `custom_field:"<name>=<value>"`

### Free text
Bare words search subject + body. Phrases in quotes: `"exact phrase"`.

### No negation
Front search has **no negation** — no `-from:`, no `NOT`, no exclusion operators.

### Max 15 filters per query.

## CLI Commands

### Scan inbox
```bash
joelclaw email inbox                          # default: is:open, 50 results
joelclaw email inbox -n 100                   # more results
joelclaw email inbox -q "is:open is:unreplied"  # awaiting reply
joelclaw email inbox --from notifications@github.com  # by sender
joelclaw email inbox --before 2026-02-01      # auto-converts to unix ts
joelclaw email inbox --after 2026-01-15       # auto-converts to unix ts
joelclaw email inbox --page-token "TOKEN"     # pagination (from previous response)
```

Flags can be combined:
```bash
joelclaw email inbox --from matt@totaltypescript.com --after 2026-02-01 -n 20
```

### Archive single
```bash
joelclaw email archive --id cnv_xxx
```

### Archive multiple by ID (fast, parallel, batched)
```bash
joelclaw email archive-ids --ids cnv_abc,cnv_def,cnv_ghi
```
Runs 10 concurrent requests. Best for triaging a page of results — grab the IDs of noise and blast them.

### Archive bulk by query (dry-run by default)
```bash
joelclaw email archive-bulk -q "is:open from:notifications@github.com"           # dry run — shows count + sample
joelclaw email archive-bulk -q "is:open from:notifications@github.com" --confirm  # execute
joelclaw email archive-bulk -q "is:open from:notifications@github.com" --limit 100 --confirm  # batch size
```

### Read a conversation
```bash
joelclaw email read --id cnv_xxx
joelclaw email read --id cnv_xxx --refresh   # bypass local cache
```

`joelclaw email read` uses a local thread + attachment cache (default TTL: 60 minutes). Cache path: `~/.cache/joelclaw/email/`. Use `--refresh` to bypass cache and force a fresh fetch from Front.

### Read output shape + jq patterns

`joelclaw email read` returns:

```text
result.conversation: {id, subject, status, from: {name, email}, date, tags}
result.messages[]: {id, from: {name, email}, date, is_inbound, body_preview (or body), attachments[]}
result.attachment_summary: {total, cached, metadata_only, cache_errors}
result.cache: {hit, source, cache_path, age_seconds, ttl_seconds}
```

Message content is currently in `body_preview`, with a migration to `body` underway. Treat both as valid for backwards compatibility.

Correct extraction pattern (current field):
```bash
joelclaw email read --id cnv_xxx 2>&1 | jq '.result.messages[] | {from: .from.email, date: .date, body: .body_preview}'
```

Backwards/forwards-compatible extraction pattern:
```bash
joelclaw email read --id cnv_xxx 2>&1 | jq '.result.messages[] | {from: .from.email, date: .date, body: (.body // .body_preview)}'
```

### List inboxes
```bash
joelclaw email inboxes
```

## Triage Workflow

### 1. Load inbox state

```bash
joelclaw email inbox -n 50
```

Parse the JSON result. Each conversation has: `id`, `subject`, `from` (name + email), `date`, `status`, `tags`.

Useful follow-up queries:
```bash
joelclaw email inbox -q "is:open is:unreplied" -n 50   # awaiting reply
joelclaw email inbox --before 2026-01-01 -n 50          # old stuff to clean
joelclaw email inbox --from notifications@github.com    # CI noise check
```

### 2. Categorize using inference

Read each conversation and **decide** its category based on sender, subject, and context. Do NOT use hardcoded domain lists. Use judgment:

- **Reply needed** — Real people expecting a response. Colleagues, collaborators, friends, business contacts with personal messages. Look for reply threads (`Re:`), questions, invitations to specific meetings, requests.
- **Read later** — Interesting content worth saving. Newsletters Joel subscribes to intentionally (The Information, Astral Codex Ten, Lenny's Newsletter), industry news, technical deep-dives.
- **Actionable** — Requires action but not a reply. Bills, security alerts, expiring trials, tax documents, delivery updates for real orders, calendar invites needing RSVP.
- **Archive** — No value. Marketing spam, promotional offers, cold outreach, duplicate notifications, resolved alerts, automated reports nobody reads, vendor upsells.

### 3. Present triage summary

Organize findings for Joel. Lead with what matters:

```
## 🔴 Reply needed (N)
- **Name** — Subject (why it needs a reply)

## ⚡ Actionable (N)
- **Sender** — Subject (what action)

## 📖 Read later (N)
- **Source** — Subject

## 🗑️ Archive candidates (N)
- Count by type (e.g., "14 marketing, 8 CI failures, 5 duplicate notifications")
```

### 4. Execute decisions

**Fastest path for noise:** scan a page, collect noise IDs, use `archive-ids`:
```bash
joelclaw email archive-ids --ids cnv_aaa,cnv_bbb,cnv_ccc,cnv_ddd
```

**For sender-based cleanup:** use `archive-bulk` with `from:` filter:
```bash
joelclaw email archive-bulk -q "is:open from:notifications@github.com" --limit 100 --confirm
```

**Rate limiting:** Front allows ~50 req/s. If archiving large batches (>200), add pauses between batches or expect 429s. The CLI handles batching for `archive-ids` (10 concurrent), but `archive-bulk` iterates page-by-page and may hit limits on very large sets.

**Read before deciding:**
```bash
joelclaw email read --id cnv_xxx
```

## Key Context

- Joel has 8 Front inboxes across joel@egghead.io, joelhooks@gmail.com, joel@skillrecordings.com, joel@badass.dev, joel.hooks@vercel.com, LinkedIn, DMs, and Inngest
- `joelclaw email inboxes` lists them all with IDs
- Joel's teammate ID: `tea_hjx3`
- The CLI handles Front API auth automatically via `secrets lease front_api_token`
- Draft-then-approve for replies — never send directly
- Front search results are eventually consistent — recently archived items may still appear in search for a few minutes
- Pagination: responses include `next_page_token` when more results exist; pass via `--page-token`
- `[aih]` subject prefix indicates AI Hero project threads via `bricks@aihero.dev` Google Group. Key participants: Alex Hillman (`alex@indyhall.org`), Matt Pocock (`mattpocockvoice@gmail.com`), Amy Hoy (`team@stackingthebricks.com`). Treat as VIP: always surface, never archive.

## Signals for "Reply Needed"

These are heuristics, not rules. Use judgment for each:

- `Re:` prefix + real person sender (not a bot/noreply)
- `[aih]` prefix — AI Hero collaboration via `bricks@aihero.dev` (Alex Hillman, Matt Pocock, Amy Hoy). VIP: always surface, never archive.
- Direct questions in subject line
- Meeting invitations from known contacts
- Threads where Joel was the last sender and someone replied
- Slack notification summaries (may indicate missed conversations worth checking)

## Signals for "Archive"

- `noreply@`, `no-reply@`, `notifications@` senders with no actionable content
- Marketing subject patterns: "LAST chance", "% off", "deal", "unlock", "limited time"
- Duplicate conversations (same subject from same sender — archive all but most recent)
- Resolved monitoring alerts (`RESOLVED` in subject)
- CI failure notifications older than 24h (stale — either fixed or won't be)
- Cold outreach from unknown senders with sales language

## Common Noise Senders (bulk-archive candidates)

These are historically high-volume, low-signal senders. Use `archive-bulk` with `from:` filter:
```
notifications@github.com        — CI failures, dependabot, PR notifications
alerts@alerts.betterstack.com   — resolved uptime alerts
no-reply@is.email.nextdoor.com  — neighborhood spam
```
Always dry-run first to verify the query matches what you expect.

Files in this skill

  • SKILL.md10.1 KB
  • agents/openai.yaml91 B
  • assets/large-logo.png5.1 KB
  • assets/small-logo.svg1.1 KB

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.