From email-tools
This skill should be used when the user asks to "check my email", "clean up my inbox", "email triage", "send an email", "reply to", "forward this", "search my email", "archive emails", "label emails", "delete emails", "unsubscribe", or invokes /email. Provides Gmail management via the gws CLI.
How this skill is triggered — by the user, by Claude, or both
Slash command
/email-tools:email-toolsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Manage Gmail entirely from the command line using the `gws` CLI.
Manage Gmail entirely from the command line using the gws CLI.
Before archiving, trashing, or deleting ANY messages:
This applies EVERY time, even if the user says "delete them", "trash all", "archive promos". Always confirm first.
When acting on 2+ messages, make exactly ONE batchModify call with all IDs in an array. Do NOT call trash/modify in a loop for each message ID.
CORRECT (one call for all messages):
gws gmail users messages batchModify --params '{"userId":"me"}' --json '{"ids":["ID1","ID2","ID3","ID4","ID5"],"addLabelIds":["TRASH"]}'
WRONG (calling trash individually for each — NEVER DO THIS):
gws gmail users messages trash --params '{"userId":"me","id":"ID1"}'
gws gmail users messages trash --params '{"userId":"me","id":"ID2"}'
gws gmail users messages trash --params '{"userId":"me","id":"ID3"}'
There are NO --user-id, --message-id, --max-results, or --label-ids flags on API methods. Everything goes inside --params as JSON.
gws gmail users messages list — NOT gws gmail list
When the user says "delete", suggest archiving instead. Only trash if they insist.
All gws commands MUST be prefixed with the active account's config directory. Before running any gws command, read the state file:
cat ~/.config/email-tools/accounts.json 2>/dev/null
Then prefix every gws command with the active account's config_dir:
GOOGLE_WORKSPACE_CLI_CONFIG_DIR=CONFIG_DIR gws gmail +triage --format json
If no state file exists, follow references/multi-account.md to set up accounts. If a default ~/.config/gws auth exists, migrate it first.
When the user says "switch account", "add account", "remove account", "use my work email", etc., follow references/multi-account.md.
All commands below assume the GOOGLE_WORKSPACE_CLI_CONFIG_DIR=CONFIG_DIR prefix is added.
gws gmail +triage [--max N] [--format json] [--query 'QUERY'] # Read-only inbox summary
gws gmail +send --to EMAIL --subject "..." --body "..." # Send (use --dry-run to preview)
gws gmail +reply --message-id MSG_ID --body "..." # Reply with auto-threading
gws gmail +reply-all --message-id MSG_ID --body "..." # Reply-all
gws gmail +forward --message-id MSG_ID --to EMAIL # Forward
gws gmail +read --id MSG_ID [--headers] # Read message content
gws gmail +watch --project PROJECT_ID # Stream new emails (NDJSON)
# Search/list messages
gws gmail users messages list --params '{"q":"QUERY","userId":"me","maxResults":500}' [--page-all]
# Get message metadata (for categorization)
gws gmail users messages get --params '{"id":"MSG_ID","userId":"me","format":"metadata","metadataHeaders":["From","To","Subject","List-Unsubscribe"]}'
# Modify labels (single message)
gws gmail users messages modify --params '{"id":"MSG_ID","userId":"me","removeLabelIds":["INBOX"]}'
# Modify labels (BULK — always use this for 2+ messages)
gws gmail users messages batchModify --params '{"userId":"me"}' --json '{"ids":["ID1","ID2",...],"removeLabelIds":["INBOX"]}'
# Trash single message
gws gmail users messages trash --params '{"userId":"me","id":"MSG_ID"}'
# Get user profile
gws gmail users getProfile --params '{"userId":"me"}'
# List labels
gws gmail users labels list --params '{"userId":"me"}'
Run these checks silently and act on the first failure.
Step 1 — Check gws installed:
which gws 2>/dev/null
If not found → follow references/setup-guide.md from Step 1.
Step 2 — Check accounts state:
cat ~/.config/email-tools/accounts.json 2>/dev/null
CONFIG_DIR from it, proceed to Step 3.gws auth status). If yes, migrate it to multi-account setup per references/multi-account.md. If no auth at all, follow references/setup-guide.md then references/multi-account.md.Step 3 — Verify active account:
GOOGLE_WORKSPACE_CLI_CONFIG_DIR=CONFIG_DIR gws gmail users getProfile --params '{"userId":"me"}'
If this fails, the token may be expired. Instruct: ! GOOGLE_WORKSPACE_CLI_CONFIG_DIR=CONFIG_DIR gws auth login -s gmail
Show: "Connected as [email protected] (personal). You have N accounts configured."
Three phases. Do not skip or combine.
Get unread count:
gws gmail users messages list --params '{"q":"is:unread in:inbox","maxResults":1,"userId":"me"}'
Check resultSizeEstimate and adapt:
Under 100 unread: Use gws gmail +triage --max 100 --format json for overview and categorize individually.
100–500 unread: Fetch all IDs with --page-all, categorize in batches of 50.
500+ unread: Use targeted queries to find noise categories first:
gws gmail users messages list --params '{"q":"is:unread in:inbox from:[email protected]","userId":"me"}' --page-all
gws gmail users messages list --params '{"q":"is:unread in:inbox list:unsubscribe","userId":"me"}' --page-all
gws gmail users messages list --params '{"q":"is:unread in:inbox from:noreply","userId":"me"}' --page-all
Show breakdown by category with counts. Let user pick which to archive in bulk.
0 unread: "Your inbox is clean!"
Apply heuristics from references/triage-heuristics.md. Assign each message to Keep, Archive, or Unsure.
Show grouped summary with numbered lists. Wait for user to tell you what to do.
When the user says to archive or delete:
batchModify call:
gws gmail users messages batchModify --params '{"userId":"me"}' --json '{"ids":["id1","id2","id3",...],"removeLabelIds":["INBOX"]}'
"addLabelIds":["TRASH"] insteadNEVER loop through messages individually. ALWAYS use batchModify.
ALWAYS preview before sending. Show the draft (recipients, subject, body) and wait for "yes".
gws gmail +send --to EMAIL --subject "..." --body "..." --dry-run # Preview
gws gmail +send --to EMAIL --subject "..." --body "..." # Send after confirmation
gws gmail +reply --message-id MSG_ID --body "..." # Reply
gws gmail +forward --message-id MSG_ID --to EMAIL # Forward
Use --draft to save without sending. Use --html for HTML content.
gws gmail users messages list --params '{"q":"from:alice subject:report after:2026/03/01","userId":"me"}'
gws gmail +read --id MSG_ID --headers
Only read message bodies when the user explicitly asks. During triage/search, use metadata only.
See references/gws-gmail-commands.md for the full Gmail query syntax cheat sheet.
gws gmail users labels list --params '{"userId":"me"}' # List labels
gws gmail users messages modify --params '{"id":"MSG_ID","userId":"me","removeLabelIds":["INBOX"]}' # Archive one
gws gmail users messages batchModify --params '{"userId":"me"}' --json '{"ids":[...],"removeLabelIds":["INBOX"]}' # Archive bulk
gws gmail users messages modify --params '{"id":"MSG_ID","userId":"me","addLabelIds":["LABEL_ID"]}' # Add label
Present all email lists as numbered items. After showing a list, accept selection by number:
3 → item 31,3,5 → items 1, 3, 51-5 → items 1 through 5all → all itemsnone / skip → no actionMaintain an internal mapping of display number → message ID. Resolve to actual IDs before executing commands.
references/setup-guide.md — First-time setup walkthrough, OAuth, troubleshootingreferences/multi-account.md — Multi-account management, adding/switching/removing accounts, state file formatreferences/gws-gmail-commands.md — Full command reference, query syntax, all parametersreferences/triage-heuristics.md — Categorization rules, batch processing, output formatnpx claudepluginhub crystal-peak/claude-plugins --plugin email-toolsManages Gmail across multiple accounts: read, search, send, reply to emails, and handle labels using Python scripts and API with structured sending workflow.
Guides Apple Mail inbox triage, organization, replies, and cleanup using MCP tools like get_inbox_overview, move_email, and batch operations for productivity workflows.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.