Skip to main content
ClaudeWave
Skill341 repo starsupdated today

gmail-watch

The gmail-watch skill powers an automated detection engine that monitors your Gmail email watches on a schedule. It polls Google Workspace for new messages matching each watch's query, filters out automated senders and self-sent mail, deduplicates results, and passes surviving matches to a shared drafting thread for reply composition. Use this skill when you want background email monitoring that automatically identifies and surfaces relevant incoming messages without manual checking.

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

SKILL.md

# Gmail Watch

This skill ships the **detection engine** behind your email watches. It is not
something you run by hand — when you create an email watch (`email_watch` tool,
`gini email add`, or the settings UI), Gini adds it to ONE shared background job
whose pre-run hook runs this skill's `detect` script on a schedule. All of your
watches share that one job and one "Email watch" chat thread; the script decides
which watches have new mail to draft a reply to.

## What it does

Each time the shared watch job fires, `detect` runs **headless** (no model turn,
no approval) and, for EACH of your watches:

1. Checks the Google sign-in (once for the session). If signed out, every watch
   reports `needs_auth` and nothing is drafted — the watches stay active and
   recover the moment you re-authenticate.
2. Lists new mail matching that watch's query via `gws`, bounded by a per-watch
   watermark so steady-state polling lists almost nothing.
3. Drops automated senders (no-reply, mailer-daemon, notifications, …) and your
   own address — only real, human, new mail survives.
4. Hands each surviving match (its sender / Subject / Date / snippet only — never
   the body), labeled by sender, to the drafting turn as quoted, untrusted data.

Each watch is independent: one sender's transient error marks only that watch and
the others keep working. When nothing new matches across all your watches, the
run ends with **no model turn at all** — the watches are silent until there is
something to act on.

## How a match becomes a reply

The detection floor reads only metadata. One drafting turn then handles all the
matches for that tick in the shared thread: it reads each full message and
composes a reply per email — each labeled by sender — through the **google-gmail**
skill, posts the proposed replies for you to review, and sends only when you
explicitly ask (which runs `gws gmail +reply` through the normal approval gate).

## Stateful but pure

`detect` is a pure function of your watch list and the small per-watch cursors it
is handed each run; it never writes to disk itself. Gini stores those cursors on
the shared watch job and advances each only after a drafting turn is dispatched,
so a match is never silently lost if delivery fails — it re-surfaces on the next
run.

## First run baselines

The first time each watch runs it records where your inbox currently is and
drafts nothing for pre-existing mail — only mail that arrives *after* the watch
was created triggers a reply. A newly added watch baselines on its own first tick,
independently of your existing watches.