Skip to main content
ClaudeWave
Skill1.8k repo starsupdated 1mo ago

google-gmail

Gmail via gws: send, read, search, label, draft, reply, forward.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/Open-Curiosity/gini-agent /tmp/google-gmail && cp -r /tmp/google-gmail/skills/google/google-gmail ~/.claude/skills/google-gmail
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Google Gmail

Use `gws gmail` to read, search, send, reply, forward, draft, label, and triage Gmail directly from the terminal. The CLI wraps the Gmail v1 API and produces structured JSON, so it composes cleanly with `jq` and other shell tooling.

## Parsing gws output

`gws` writes its JSON to **stdout** and a `Using keyring backend: keyring` preamble (plus any warnings) to **stderr**. `terminal_exec` already shows the two streams as separate blocks, so on its own the preamble is harmless — but the moment you pipe `gws` into a JSON parser you must strip stderr first, or the preamble lands on the JSON and the parse throws:

```bash
gws ... 2>/dev/null | jq ...                    # correct: stderr dropped before the pipe
gws ... 2>/dev/null | python3 -c 'import sys,json; json.load(sys.stdin)'
```

Never use `2>&1` when piping into a parser — it folds the preamble onto the JSON and breaks it. Do not pass `--format text` either; it is invalid (valid formats are `json`, `table`, `yaml`, `csv`), and the raw `users.*` API already defaults to JSON. When you just need the data and don't need to parse it yourself, prefer the curated `+helpers`, which print clean output. Note that `2>/dev/null` also discards gws's own error messages, so if a command returns empty or unexpected output, re-run it without the redirect (or check the exit code) to see the actual error.

## Prerequisites

- If this deployment is managed/hosted, the Google credential is already provisioned at sign-in — `gws` is installed and authenticated, so skip the setup flow below and run `gws` directly. Scopes are fixed at sign-in on a managed deployment: when a call fails with `scope required` or HTTP 401, tell the user which action needs a scope their account wasn't granted, instead of trying to set anything up. (The scope list below still describes which verb needs which scope.)
- `gws` installed and authenticated. If `gws` is not on PATH OR `gws auth status` reports no authenticated user, do NOT silently call setup. Instead, in a single short reply to the user:
  1. State plainly what's missing — e.g. "Google Workspace access isn't set up on this machine yet" or "your Google sign-in has expired."
  2. Ask one sentence: "Want me to walk you through setting it up?" Wait for the user's answer.
  3. If they say yes, call `read_skill` with name `google-workspace-setup` and run that skill's onboarding flow turn-by-turn. If they say no or ask to defer, acknowledge briefly and stop — do not retry the original request.
- Apply the same flow when any `gws gmail ...` call fails mid-task with `command not found` / ENOENT, HTTP 401, "no credentials", or "scope required". Don't report the failure as a dead end — surface the missing prerequisite and ask if the user wants to set it up before moving on.
- The OAuth scopes the user picked at login must cover the verbs the agent will use:
  - Read-only triage: `gmail.readonly`
  - Send a new message: `gmail.send`
  - Reply, reply-all, forward: `gmail.modify` — upstream helpers fetch the original message to thread `In-Reply-To` / `References` headers, which `gmail.send` alone cannot do
  - Drafts and labels: `gmail.modify` (or `https://mail.google.com/` for full access including permanent delete)
  - Watch for new mail (`+watch`): `gmail.modify` AND `https://www.googleapis.com/auth/pubsub` — Cloud Pub/Sub is a separate Google API and its scope must be granted alongside the Gmail scope

## Selecting a Google account

The connected Google accounts (each with its tag, email, and config dir) are listed in your system context under **"Connected Google accounts"**. To target a specific account, prefix the command with its config dir:

```bash
GOOGLE_WORKSPACE_CLI_CONFIG_DIR="<configDir>" gws gmail +triage
```

Selection rule: one account connected → just use it. Two or more:

- The user named or clearly implied one account (a tag, an email, or unambiguous context) → use only that account.
- A read/lookup/search the user didn't tie to an account (e.g. listing events, searching mail, finding a doc) → run it against **every** connected account (one `gws` call per config dir) and aggregate, labeling each result by its tag and email. Don't pick just one, and don't ask — the user wants the whole picture across accounts.
- A write (send, create, edit, delete) with no account named → ASK which account first; never guess.

If no accounts are connected yet, fall back to the setup flow in Prerequisites (`read_skill` with `google-workspace-setup`). On a managed/hosted deployment an account is always connected, so this case doesn't arise.

## When to Use

- The user asks Gini to send, draft, read, search, label, reply to, or forward email.
- Summarizing or triaging the inbox (latest unread, by-sender, by-label digests).
- Saving an attachment from a thread, or pulling a message body into another workflow.
- Watching for new messages and streaming them as NDJSON (`gws gmail +watch`).

## When NOT to Use

- Agent-internal scratch notes or transient state — use the `memory` tool, not email-to-self.
- Personal to-dos that should appear on the user's iPhone — use `apple-reminders`.
- Cross-device personal note-taking — use `apple-notes` or `obsidian`.
- Calendar invites and meeting scheduling — use `google-calendar` (a Gmail invite is still a Calendar event).
- Bulk outbound mail (newsletters, marketing) — personal Gmail has aggressive sending limits and Google will throttle or suspend the account. Tell the user to use a transactional provider.

## Quick Reference

The Gmail surface in `gws` is split into auto-generated API methods (`gws gmail users messages list`, `gws gmail users labels create`, …) plus a small set of curated helpers (`+send`, `+reply`, `+read`, `+triage`, …) that handle MIME encoding, threading, and base64 for you. Prefer the helpers for everyday tasks. The raw API is rooted at the `users` resource — every `--params` JSON must include `"userId": "me"` (or another delegated address).

### Send

```ba