skills/gmail-draft/SKILL.md
Create a draft email in Gmail from provided email data
npx skillsauth add sofer/.agents gmail-draftInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create a draft email in the user's Gmail account. The draft is saved for human review — this skill never sends emails. It accepts already-composed email content and passes it through to the Gmail API.
This skill uses the existing Gmail draft script and OAuth credentials at ~/code/airmail/. The following files must exist:
~/code/airmail/gmail_draft.py — the draft creation script~/code/airmail/client_secret.json — Google OAuth client credentials~/code/airmail/gmail_token.json — OAuth token (auto-refreshes)~/code/airmail/email-signature.html — email signature (appended automatically)Check that the following fields are present in the input:
If any of these are missing or empty, stop and report which field(s) are missing. Do not proceed.
Use the existing gmail_draft.py script in ~/code/airmail/:
For a reply in an existing thread:
python3 ~/code/airmail/gmail_draft.py "<thread_id>" "<to>" "<body>"
For a new conversation:
python3 ~/code/airmail/gmail_draft.py new "<to>" "<subject>" "<body>"
The script:
email-signature.htmlOn success: Report the result with:
On error: Report the result with:
Do not attempt to retry or work around errors. Report them as-is.
If the script is unavailable or a more complex draft is needed (e.g. with CC/BCC), use the Gmail API directly:
import os
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from email.mime.text import MIMEText
import base64
TOKEN_FILE = os.path.expanduser("~/code/airmail/gmail_token.json")
creds = Credentials.from_authorized_user_file(TOKEN_FILE)
if creds.expired and creds.refresh_token:
creds.refresh(Request())
with open(TOKEN_FILE, "w") as f:
f.write(creds.to_json())
service = build("gmail", "v1", credentials=creds)
# Build the message
message = MIMEText(body)
message["to"] = to
message["subject"] = subject
if cc:
message["cc"] = cc
if in_reply_to:
message["In-Reply-To"] = in_reply_to
message["References"] = references or in_reply_to
raw = base64.urlsafe_b64encode(message.as_bytes()).decode()
draft_body = {"message": {"raw": raw}}
if thread_id:
draft_body["message"]["threadId"] = thread_id
draft = service.users().drafts().create(userId="me", body=draft_body).execute()
to, subject, or body is missing, stop and report which required field(s) are absent~/code/airmail/thread_id is invalid, do not fall back to creating an unthreaded draft — report the errorOn success:
status: created
draft_id: {id from API}
thread_id: {thread id, if threaded}
summary: Draft created: {subject} → {to}
On error:
status: error
error: {error description}
tools
Check whether Claude and Codex have equivalent access to shared agent resources, skills, hooks, plugins, MCP servers, permissions, startup behaviour, and provider-specific adapter config. Use when comparing agent environments, debugging missing capabilities after restart, or deciding whether to symlink a resource or configure a runtime.
testing
Record substantive skill use in an append-only local log. Use after choosing or invoking a non-system skill for real work, when a skill is inspected but not used, or when a skill fails to apply. Do not use for routine system skills or incidental file reads.
testing
Turn a vague or underspecified request into a self-contained problem statement. Use when the user has a rough idea, when a request would fail if handed directly to an agent, or before non-trivial work that needs shared understanding.
data-ai
Append a one-line learning to ~/.agents/learning-log.md. Use when the user types /learning, or when something genuinely worth remembering surfaced during work and the user confirms it should be captured.