---
name: mail-organize
description: Use this skill whenever the user asks to search, review, tag, label, archive, or otherwise organize their email. Covers questions like "what's in my inbox", "clean up my mail", "find messages about X", "tag/label these emails", or "archive old newsletters". This skill governs ALL interaction with the user's mail via the mailctl tool. Do not use raw notmuch, mbsync, or any mail-related shell command outside of mailctl for this task.
allowed-tools: Bash(python3 ~/bin/mailctl.py:*)
---

# Mail organization via mailctl

The user has several mail accounts synced locally via mbsync/notmuch. The
`mailctl` CLI (`~/bin/mailctl.py`) is the ONLY sanctioned interface for
you to read or organize this mail. Read the hard rules below before
running anything.

## Hard rules, no exceptions

1. **Never send or actually deliver mail, under any framing.** mailctl
   has no code path that talks to SMTP, and that's intentional, it
   cannot send regardless of what you're asked to do. Drafting is
   allowed and has a real command for it (see below), but a draft is
   not a send: it sits in the Drafts folder until the user opens it in
   neomutt and sends it themselves. If the user asks you to send
   something outright, write it as a draft instead and tell them it's
   waiting for their review, don't look for another way to deliver it.

2. **Never sync mail.** Do not run `mbsync`, `mailsync.sh`, or
   `notmuch new` for any reason, even if the user's request seems to
   imply "fresh data would help." Mail sync runs on the user's own
   hourly cron job. If you suspect the local index is stale, say so
   and ask the user to sync manually, don't do it yourself.

3. **Always scope mutations to a single account**, using
   `--account <name>`. The valid account names come from the user's
   own config, not from this file; run `mailctl count '*' --help` (or
   any subcommand's `--help`) to see the current list, and use one of
   those names exactly.

   Do not use `--all-accounts` unless the user has explicitly and
   specifically asked for a change across every mailbox in this same
   conversation turn. If a task seems like it might span accounts,
   ask which account first rather than defaulting to all of them.

4. **Always dry-run before applying.** Run `mailctl tag` without
   `--apply` first, show the user the preview (query, match count,
   proposed change), and only add `--apply` after they confirm, unless
   the user has already given blanket approval for this specific,
   narrowly-described cleanup task in this conversation.

5. **Never pass `--confirm-destructive` on your own judgment.** If a
   tag operation is flagged destructive (adding deleted/trash/spam, or
   removing inbox), stop and ask the user explicitly. Don't reason your
   way into deciding a destructive change is obviously fine.

6. **Never raise `--max-messages` on your own judgment.** `tag --apply`
   aborts if the match exceeds the cap (default 5000). If a legitimate
   bucket is genuinely that large, show the user the count and ask
   before re-running with a higher `--max-messages`. Don't silently bump
   the cap to push a big batch through.

7. **Reads are fine to run freely, including globally** (no
   `--account`): `search`, `show`, `tags`, `count`, `senders`, `subjects`. They're
   read-only, and non-scoped reads across all mail are a legitimate,
   frequently-useful thing the user wants. The scoping requirement in
   rule 3 applies to `tag` only.

## Typical workflow

1. Understand what the user wants organized (a sender, a topic, a date
   range, a specific account). To find *which* senders are clogging a
   mailbox, use `mailctl senders "<query>" [--account NAME] [--top N]`
   rather than guessing addresses and probing them one at a time with
   `count`. When one sender needs splitting (receipts vs marketing),
   use `mailctl subjects "<query>" --account NAME` to see which terms
   actually cover the bucket instead of guessing subject keywords.
2. `mailctl search "<query>" [--account NAME]` to see what matches
   and confirm the query is catching the right messages, and only
   those.
3. `mailctl tag "<query>" --account NAME --add X --remove Y` (no
   `--apply`) to preview the change.
4. Show the user the preview verbatim. Wait for confirmation.
5. Re-run with `--apply` only after confirmation.

## Command reference

```
mailctl search "<notmuch query>" [--account NAME] [--json]
mailctl show "<thread:id or id:msgid>"
mailctl tags
mailctl count "<query>" [--account NAME]
mailctl senders "<query>" [--account NAME] [--top N] [--json]
mailctl subjects "<query>" [--account NAME] [--top N] [--json]
mailctl tag "<query>" --account NAME [--add TAG]... [--remove TAG]...
              [--apply] [--confirm-destructive] [--max-messages N]
mailctl draft --account NAME --to ADDR --subject TEXT
              [--cc ADDR] [--body TEXT | --body-file PATH]
```

## Drafting mail

`mailctl draft` writes a real message into the account's local Drafts
maildir. It does not send anything, ever, there is no `--apply` step
because a draft has no side effect worth confirming beyond writing it.

Rules specific to drafting:

- `--account` is always required here too, same reasoning as `tag`:
  the draft's `From` address is derived from the account, so an
  ambiguous or wrong account means a draft that would send from the
  wrong identity.
- After writing a draft, tell the user plainly: it's local until the
  next mbsync run, it is not sent, and they need to open it in
  neomutt (that account's Drafts folder, whatever it is named there,
  or the postponed message list if configured) to review, edit, and
  send it.
- If the user's request is vague about recipient or content ("draft
  something to my accountant about the invoice"), ask for the missing
  specifics rather than guessing a plausible-sounding email address or
  inventing content they didn't ask for.
- Never draft and then take any further action on that draft, no
  tagging it, no re-reading it back to "double check", the task ends
  when the draft is written and reported.

Notmuch query syntax reference: `from:`, `to:`, `subject:`, `tag:`,
`date:`, boolean `and`/`or`/`not`. When in doubt about whether a query
is too broad, run `count` first before `tag`.

## What NOT to do, even if it seems helpful

- Don't infer that a broad cleanup request ("archive my old newsletters")
  means every account, ask which account or offer to do them one at a
  time.
- Don't chain multiple `--apply` tag operations without showing the
  user each preview individually, batch confirmation of unreviewed
  changes defeats the point of the dry-run step.
- Don't try to work around the no-sync rule by suggesting the user run
  a sync "so I can help better" unless they ask whether fresher data
  would help, that's information, not you initiating an action.
