sync

Sync the Gmail filter set to match filters.js — dry run first, then apply. Owns the 🔍 and 💾 title prefixes across that pair, so other sessions can see a live write in flight. Use when asked to sync, a

SKILL.md

GitHub

Sync (filters.js → the live Gmail filter set)

src/sync.js is a dry run by default; --apply performs the deletes and creates. This skill is the two of them as one operation, and it exists because the stretch between them is the only thing in this repo another session acts on. See AGENTS.md (Repo → Session titles).

filter and archive end by syncing — they invoke this skill rather than calling sync.js directly, so the prefixes go on wherever a sync happens.

1. 🔍 before the dry run

Read the session's title (mcp__ccd_session_mgmt__get_session with "self") and set it back with a 🔍 prefix (mcp__ccd_session_mgmt__set_session_title), replacing any existing lifecycle prefix rather than stacking. In a cloud session, where those do not exist, mcp__claude-code-remote__get_session with no session_id describes this session (ccr.id, ccr.title), and mcp__claude-code-remote__set_session_title takes that id. Skip this only when neither tool exists. Do not report this.

Then:

node src/sync.js

Read the plan. Nothing is written yet.

2. Check the sidebar before applying

--apply takes a lock, but the dry run does not, so the plan just printed is stale the moment another session applies. List sessions (mcp__ccd_session_mgmt__list_sessions) and look for another 💾 whose cwd is an email-filter-builder worktree — a 💾 in another repo writes some other resource and does not contend for this account. If one is there, wait rather than applying — and re-run step 1 afterwards, because its plan was computed against the account as it was.

Re-run step 1 too if anything else happened between the dry run and here.

3. 💾 across the apply

Swap the prefix to 💾 before the command, not after it returns — the warning is only useful while the write is in flight. Do not report this.

GMAIL_SYNC_SESSION_TITLE='<this session title>' node src/sync.js --apply

Pass the title read in step 1, single-quoted (an apostrophe in it becomes '\''). The lock records it beside the session id, so a session refused while this one applies prints a link to this chat by name.

If the apply refuses because another session holds the lock, tell the user which chat it is by pasting the link line it printed — [title](#local_…) — verbatim; the app renders it as a link that opens that session. When there is no link line (a terminal session, or a lock taken before ids were recorded), name the holder by its title, branch and worktree instead. Never build a link from a session id you did not read from that output. Put the title back to what it was before the 💾 — this session is not writing — and wait, then re-run step 1.

It prompts before a destructive run and refuses without a terminal to ask; --yes skips the prompt. If it stops for that confirmation and the user has to click, the title stays 💾 while it waits — a write in flight outranks a park, and handing back mid-apply is still mid-apply.

4. Whatever stage the branch reached

Once it returns, replace 💾 with what is true now: 📦 if the change is gated and shippable, ⏳ if there is more to do, 🚙 if it waits on the user. Never leave 💾 on a session that has stopped writing — another session reads it as a live write and waits on nothing. Do not report this.

If the apply failed, say so with its output and put the title back the same way. A partial apply is possible: re-run step 1 to see what the account actually holds rather than assuming the plan applied or did not.