{"slug":"ops-comms","title":"ops-comms","summary":"Send and read messages across all channels. Routes based on arguments — whatsapp, email, slack, telegram, discord, notion, or natural language like \"send [msg] to [contact]\".","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-14T20:43:18.632581Z","repo":{"url":"https://github.com/davepoon/buildwithclaude","stars":3556,"forks":535,"license":"MIT","updatedAt":"2026-09-27T03:56:38Z"},"bodyHtml":"<hr>\n<p>name: ops-comms\ndescription: Send and read messages across all channels. Routes based on arguments — whatsapp, email, slack, telegram, discord, notion, or natural language like \"send [msg] to [contact]\".\nargument-hint: \"[channel] | send [message] to [contact] | read [channel] | notion [search query]\"\nallowed-tools:</p>\n<ul>\n<li>Bash</li>\n<li>Read</li>\n<li>Grep</li>\n<li>Glob</li>\n<li>Skill</li>\n<li>AskUserQuestion</li>\n<li>mcp__claude_ai_Gmail__search_threads</li>\n<li>mcp__claude_ai_Gmail__get_thread</li>\n<li>mcp__claude_ai_Gmail__create_draft</li>\n<li>mcp__claude_ai_Slack__slack_send_message</li>\n<li>mcp__claude_ai_Slack__slack_read_channel</li>\n<li>mcp__claude_ai_Slack__slack_search_users</li>\n<li>mcp__claude_ai_Slack__slack_search_public_and_private</li>\n<li>mcp__claude_ops_telegram__send_message</li>\n<li>mcp__claude_ops_telegram__get_updates</li>\n<li>mcp__claude_ops_telegram__list_chats</li>\n<li>mcp__claude_ai_Notion__notion-search</li>\n<li>mcp__claude_ai_Notion__notion-fetch</li>\n<li>mcp__claude_ai_Notion__notion-get-comments</li>\n<li>mcp__claude_ai_Notion__notion-create-comment</li>\n<li>mcp__claude_ai_Notion__notion-update-page</li>\n<li>mcp__claude_ai_Notion__notion-create-pages\neffort: medium\nmaxTurns: 40</li>\n</ul>\n<hr>\n<h1>OPS ► COMMS</h1>\n<h2>Runtime Context</h2>\n<p>Before executing, load available context:</p>\n<ol>\n<li><p><strong>Daemon health</strong>: Read <code>${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/daemon-health.json</code></p>\n<ul>\n<li>Check <code>wacli-sync</code> status before any WhatsApp operation</li>\n<li>Also check <code>~/.wacli/.health</code> — if not <code>status=connected</code>, surface auth issue before proceeding</li>\n</ul>\n</li>\n<li><p><strong>Ops memories</strong>: Before drafting any message, check <code>${CLAUDE_PLUGIN_DATA_DIR}/memories/</code>:</p>\n<ul>\n<li><code>contact_*.md</code> — load profile for the recipient</li>\n<li><code>preferences.md</code> — match user's communication style, language, and tone</li>\n<li><code>donts.md</code> — restrictions that must not appear in any draft</li>\n</ul>\n</li>\n<li><p><strong>Preferences</strong>: Read <code>${CLAUDE_PLUGIN_DATA_DIR}/preferences.json</code> for <code>default_channels</code> to determine which channel to prefer when multiple are available for a contact.</p>\n</li>\n</ol>\n<h2>CLI/API Reference</h2>\n<h3>wacli (WhatsApp)</h3>\n<p><strong>Health file</strong> — check <code>~/.wacli/.health</code> BEFORE any wacli command:</p>\n<ul>\n<li><code>status=connected</code> → proceed</li>\n<li><code>status=needs_auth</code> or <code>status=needs_reauth</code> → prompt user for QR scan, do NOT run wacli commands</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>Command</th>\n<th>Usage</th>\n<th>Output</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>wacli doctor --json</code></td>\n<td>Check auth/connected/lock/FTS</td>\n<td><code>{data: {authenticated, connected, lock_held, fts_enabled}}</code></td>\n</tr>\n<tr>\n<td><code>wacli chats list --json</code></td>\n<td>All chats</td>\n<td><code>{data: [{JID, Name, Kind, LastMessageTS}]}</code></td>\n</tr>\n<tr>\n<td><code>wacli messages list --chat \"&lt;JID&gt;\" --limit N --json</code></td>\n<td>Messages for chat</td>\n<td><code>{data: {messages: [{FromMe, Text, Timestamp, SenderName, ChatName}]}}</code></td>\n</tr>\n<tr>\n<td><code>wacli messages search --query \"&lt;text&gt;\" --json</code></td>\n<td>FTS search</td>\n<td>Same as above</td>\n</tr>\n<tr>\n<td><code>wacli contacts --search \"&lt;name&gt;\" --json</code></td>\n<td>Contact lookup</td>\n<td>Contact objects</td>\n</tr>\n<tr>\n<td><code>wacli send --to \"&lt;JID&gt;\" --message \"&lt;msg&gt;\"</code></td>\n<td>Send text</td>\n<td>Success/error</td>\n</tr>\n</tbody>\n</table>\n<h3>gog CLI (Gmail/Calendar)</h3>\n<table>\n<thead>\n<tr>\n<th>Command</th>\n<th>Usage</th>\n<th>Output</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>gog gmail search \"in:inbox\" --max 50 -j --results-only --no-input</code></td>\n<td>Search inbox</td>\n<td>JSON array of threads</td>\n</tr>\n<tr>\n<td><code>gog gmail thread get &lt;threadId&gt; -j</code></td>\n<td>Get full thread with all messages</td>\n<td>Full message JSON</td>\n</tr>\n<tr>\n<td><code>gog gmail send --to \"user@example.com\" --subject \"subj\" --body \"text\"</code></td>\n<td>Send new email</td>\n<td>Send result</td>\n</tr>\n<tr>\n<td><code>gog gmail send --reply-to-message-id &lt;msgId&gt; --reply-all --body \"text\"</code></td>\n<td>Reply all</td>\n<td>Send result</td>\n</tr>\n<tr>\n<td><code>gog gmail send --to \"a@b.com\" --subject \"subj\" --body \"text\" --attach /path/file</code></td>\n<td>With attachment</td>\n<td>Send result</td>\n</tr>\n<tr>\n<td><code>gog gmail archive &lt;messageId&gt; ... --no-input --force</code></td>\n<td>Archive messages</td>\n<td>Archive result</td>\n</tr>\n</tbody>\n</table>\n<hr>\n<p>Parse <code>$ARGUMENTS</code> and route immediately:</p>\n<h2>Routing table</h2>\n<table>\n<thead>\n<tr>\n<th>Pattern</th>\n<th>Action</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>whatsapp</code></td>\n<td>Show WhatsApp recent chats — offer to read or send</td>\n</tr>\n<tr>\n<td><code>email</code></td>\n<td>Show recent email threads via Gmail MCP</td>\n</tr>\n<tr>\n<td><code>slack</code></td>\n<td>Show recent Slack activity</td>\n</tr>\n<tr>\n<td><code>telegram</code></td>\n<td>Show Telegram recent chats</td>\n</tr>\n<tr>\n<td><code>discord</code></td>\n<td>Show recent Discord channel activity (via bin/ops-discord)</td>\n</tr>\n<tr>\n<td><code>notion</code></td>\n<td>Search Notion workspace — pages, comments, tasks</td>\n</tr>\n<tr>\n<td><code>send * to *</code></td>\n<td>Parse message and contact, determine best channel, send</td>\n</tr>\n<tr>\n<td><code>read *</code></td>\n<td>Read the specified channel or contact's messages</td>\n</tr>\n<tr>\n<td>(empty)</td>\n<td>Show channel picker menu</td>\n</tr>\n</tbody>\n</table>\n<p>Natural-language parsing: phrases like <code>send \"deploy done\" to #general on discord</code> or <code>to #ops-alerts on Discord</code> should resolve to the <code>discord</code> branch below. Extract the channel token (the word after <code>#</code>, case-insensitive) and pass it as the first arg to <code>bin/ops-discord send</code>.</p>\n<hr>\n<h2>Send flow: <code>send [message] to [contact]</code></h2>\n<ol>\n<li>Parse contact name and message from <code>$ARGUMENTS</code>.</li>\n<li>Determine channel by contact lookup:\n<ul>\n<li>Check WhatsApp: <code>wacli contacts --search \"[contact]\" --json 2&gt;/dev/null</code></li>\n<li>Check Slack: <code>mcp__claude_ai_Slack__slack_search_users</code> with <code>query: \"[contact]\"</code></li>\n<li>Check email: known from context or ask</li>\n</ul>\n</li>\n<li>If multiple channels found, use <code>AskUserQuestion</code>: <code>[WhatsApp]</code> / <code>[Slack]</code> / <code>[Email]</code></li>\n<li><strong>Always preview before sending.</strong> Use <code>AskUserQuestion</code> to confirm:</li>\n</ol>\n<pre><code>Ready to send via [channel]:\n  To: [contact name] ([identifier])\n  Message: \"[full message text]\"\n\n  [Send now]  [Edit message]  [Cancel]\n</code></pre>\n<p>If user picks \"Edit message\", use <code>AskUserQuestion</code> with free-text to get the revised message, then re-preview.</p>\n<ol start=\"5\">\n<li>Send via the chosen channel. Confirm with: <code>Sent to [contact] via [channel] ✓</code></li>\n</ol>\n<h3>WhatsApp send</h3>\n<p><strong>CRITICAL — READ BEFORE SENDING:</strong> Before drafting ANY WhatsApp reply, you MUST:</p>\n<ol>\n<li>Read the full conversation: <code>wacli messages list --chat \"&lt;JID&gt;\" --limit 20 --json</code></li>\n<li>Understand which messages are from the user (<code>FromMe: true</code>) vs the contact</li>\n<li>Summarize what the conversation is about and what the contact is asking</li>\n<li>Only THEN draft a reply that addresses what the contact actually said</li>\n</ol>\n<p><strong>Never send a reply based on a single message.</strong> A message like \"can you pull it from Klaviyo?\" means nothing without knowing what \"it\" refers to from prior messages.</p>\n<p><strong>Pre-flight:</strong> Before any wacli command, check <code>~/.wacli/.health</code>. If <code>status=needs_auth</code> or <code>status=needs_reauth</code>, prompt the user: \"WhatsApp needs re-authentication. Run <code>wacli auth</code> in a separate terminal and scan the QR code, then type 'done'.\" Use <code>AskUserQuestion</code>: <code>[Done — re-paired]</code>, <code>[Skip WhatsApp]</code>. On Done, restart daemon: <code>launchctl kickstart -k gui/$(id -u)/com.claude-ops.wacli-keepalive</code>, wait 5s.</p>\n<pre><code>wacli send --to \"[contact]\" --message \"[message]\"\n</code></pre>\n<h3>Slack send</h3>\n<p>Use <code>mcp__claude_ai_Slack__slack_send_message</code> with resolved channel/user ID.</p>\n<h3>Email send (draft)</h3>\n<p>Use <code>mcp__claude_ai_Gmail__create_draft</code> — always create draft first. Then use <code>AskUserQuestion</code>:</p>\n<pre><code>Draft created for [recipient]:\n  Subject: [subject]\n  Body: [preview]\n\n  [Send now]  [Keep as draft]  [Edit]\n</code></pre>\n<hr>\n<h2>Read flow: <code>read [channel]</code></h2>\n<p><strong>WhatsApp:</strong></p>\n<pre><code>wacli chats --limit 10 --json 2&gt;/dev/null\n</code></pre>\n<p>Show last 10 chats with sender, preview, timestamp.</p>\n<p><strong>Email:</strong>\nUse <code>mcp__claude_ai_Gmail__search_threads</code> with <code>query: \"in:inbox\"</code> (NOT <code>is:unread</code> — scan full inbox including read messages), show thread list.</p>\n<p><strong>Slack:</strong>\nUse <code>mcp__claude_ai_Slack__slack_search_public_and_private</code> with <code>query: \"in:channel\"</code> (NOT <code>is:unread</code> — scan full recent activity).</p>\n<p><strong>Telegram:</strong>\nUse <code>mcp__claude_ops_telegram__get_updates</code> (limit: 20) and <code>mcp__claude_ops_telegram__list_chats</code>.\nFall back to: <code>telegram-cli --exec \"dialog_list\" 2&gt;/dev/null || echo \"Telegram MCP not configured\"</code></p>\n<p><strong>Discord:</strong>\n<code>${CLAUDE_PLUGIN_ROOT}/bin/ops-discord read \"&lt;CHANNEL_ID&gt;\" --limit 20 --json</code> — requires <code>DISCORD_BOT_TOKEN</code> (or credential-store <code>discord/bot-token</code>). Fall back to <code>bin/ops-discord channels --json</code> if the user doesn't know the channel ID and <code>DISCORD_GUILD_ID</code> is set.</p>\n<p><strong>Notion:</strong>\nUse <code>mcp__claude_ai_Notion__notion-search</code> with the user's query (or <code>query: \"\"</code> sorted by <code>last_edited_time</code> for general browsing). For each result:</p>\n<ul>\n<li>Fetch full page content with <code>mcp__claude_ai_Notion__notion-fetch</code> using the page URL/ID from search results</li>\n<li>Get comments with <code>mcp__claude_ai_Notion__notion-get-comments</code></li>\n<li>Show page title, database name, last editor, and recent comments</li>\n</ul>\n<p><strong>Notion API fallback:</strong> If MCP tools fail and <code>NOTION_API_KEY</code> is set, use <code>curl -s -H \"Authorization: Bearer $NOTION_API_KEY\" -H \"Notion-Version: 2022-06-28\" -X POST https://api.notion.com/v1/search -d '{\"query\":\"&lt;QUERY&gt;\",\"page_size\":10}'</code></p>\n<h3>Notion comment/reply</h3>\n<p>Use <code>mcp__claude_ai_Notion__notion-create-comment</code> with the page ID to reply to a comment thread. For creating new pages in a database, use <code>mcp__claude_ai_Notion__notion-create-pages</code>.</p>\n<p>Always preview before commenting:</p>\n<pre><code>Ready to comment on Notion page:\n  Page: [page title]\n  Comment: \"[comment text]\"\n\n  [Post comment]  [Edit]  [Cancel]\n</code></pre>\n<h3>Telegram send</h3>\n<p>Use <code>mcp__claude_ops_telegram__send_message</code> with <code>chat_id</code> (from list_chats) and <code>text</code>.</p>\n<h3>Discord send</h3>\n<p>Shell out to <code>bin/ops-discord send</code>. Three invocation shapes:</p>\n<pre><code># By channel alias (resolves DISCORD_WEBHOOK_&lt;UPPER&gt; or DISCORD_WEBHOOK_URL)\n${CLAUDE_PLUGIN_ROOT}/bin/ops-discord send \"&lt;channel-alias&gt;\" \"&lt;message&gt;\" --json\n\n# By channel snowflake (17-20 digit ID, routed through bot token)\n${CLAUDE_PLUGIN_ROOT}/bin/ops-discord send \"&lt;CHANNEL_ID&gt;\" \"&lt;message&gt;\" --json\n\n# By full webhook URL (useful when the URL is stored per-project)\n${CLAUDE_PLUGIN_ROOT}/bin/ops-discord send \"https://discord.com/api/webhooks/&lt;ID&gt;/&lt;TOKEN&gt;\" \"&lt;message&gt;\" --json\n</code></pre>\n<p>If the script exits 1 with <code>{\"error\":\"no discord credential configured — run /ops:setup discord\"}</code>, prompt the user via <code>AskUserQuestion</code> (≤4 options per Rule 1): <code>[Run /ops:setup discord]</code> / <code>[Paste webhook URL now]</code> / <code>[Skip]</code>. Do NOT silently skip — that violates Rule 3.</p>\n<p>Note: <code>DISCORD_WEBHOOK_URL</code> is shared with the ops-fires notification sink (<code>scripts/ops-notify.sh</code>). When pre-existing, prefer it as the default for <code>/ops:comms discord send</code> rather than asking the user to set a separate value.</p>\n<hr>\n<h2>Empty arguments — channel picker</h2>\n<p>Display the header, then use <strong>batched AskUserQuestion calls</strong> (max 4 options each):</p>\n<pre><code>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n OPS ► COMMS\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n</code></pre>\n<p><strong>Before presenting options</strong>, read <code>${CLAUDE_PLUGIN_DATA_DIR}/preferences.json</code> and check which channels are configured. Only show configured channels. If &lt;=4 total options (configured channels + \"Send a message\"), present in a single call. If &gt;4, batch:</p>\n<p>AskUserQuestion call 1 — Read channels:</p>\n<pre><code>  [Read WhatsApp]\n  [Read Email]\n  [Read Slack]\n  [More...]\n</code></pre>\n<p>AskUserQuestion call 2 (only if \"More...\"):</p>\n<pre><code>  [Read Telegram]\n  [Send a message]\n</code></pre>\n<p>If all channels are configured, that's 5 options — always batch. If only 3 channels are configured, \"Read X\" + \"Read Y\" + \"Read Z\" + \"Send a message\" = 4, fits in one call.</p>\n<p>Execute the selected action.</p>\n","files":[{"path":"SKILL.md","sizeBytes":11383,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-14T20:43:26.653859Z","sha256":"FB4E81D3EC43A87477D1C92B28CE847B8295B1490E72CA278271A2436DE346E1","sizeBytes":4495},"review":null,"source":{"repositoryUrl":"https://github.com/davepoon/buildwithclaude","path":"plugins/claude-ops/skills/ops-comms","license":"MIT","commit":"6178ae2b0900b3600419853206de1dddf0f28311","subtreeSha":"693BECC2A2DF40CF865793AA743025F52BF4E8896A5E3BCF93A3634B80D7245B","lastSyncedAt":"2026-09-27T19:47:46.68834Z"},"reviewedAt":"2026-09-14T20:43:34.957119Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/davepoon/buildwithclaude/tree/main/plugins/claude-ops/skills/ops-comms"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install davepoon-buildwithclaude@llmmart"},{"target":"git","command":"git clone https://github.com/davepoon/buildwithclaude.git"}]}