Claude-ops ops-comms
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]".
git clone https://github.com/Lifecycle-Innovations-Limited/claude-ops
T=$(mktemp -d) && git clone --depth=1 https://github.com/Lifecycle-Innovations-Limited/claude-ops "$T" && mkdir -p ~/.claude/skills && cp -r "$T/claude-ops/skills/ops-comms" ~/.claude/skills/lifecycle-innovations-limited-claude-ops-ops-comms && rm -rf "$T"
claude-ops/skills/ops-comms/SKILL.mdOPS ► COMMS
Runtime Context
Before executing, load available context:
-
Daemon health: Read
${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/daemon-health.json- Check
status before any WhatsApp operationwacli-sync - Also check
— if not~/.wacli/.health
, surface auth issue before proceedingstatus=connected
- Check
-
Ops memories: Before drafting any message, check
:${CLAUDE_PLUGIN_DATA_DIR}/memories/
— load profile for the recipientcontact_*.md
— match user's communication style, language, and tonepreferences.md
— restrictions that must not appear in any draftdonts.md
-
Preferences: Read
for${CLAUDE_PLUGIN_DATA_DIR}/preferences.json
to determine which channel to prefer when multiple are available for a contact.default_channels
CLI/API Reference
wacli (WhatsApp)
Health file — check
~/.wacli/.health BEFORE any wacli command:
→ proceedstatus=connected
orstatus=needs_auth
→ prompt user for QR scan, do NOT run wacli commandsstatus=needs_reauth
| Command | Usage | Output |
|---|---|---|
| Check auth/connected/lock/FTS | |
| All chats | |
| Messages for chat | |
| FTS search | Same as above |
| Contact lookup | Contact objects |
| Send text | Success/error |
gog CLI (Gmail/Calendar)
| Command | Usage | Output |
|---|---|---|
| Search inbox | JSON array of threads |
| Get full thread with all messages | Full message JSON |
| Send new email | Send result |
| Reply all | Send result |
| With attachment | Send result |
| Archive messages | Archive result |
Parse
$ARGUMENTS and route immediately:
Routing table
| Pattern | Action |
|---|---|
| Show WhatsApp recent chats — offer to read or send |
| Show recent email threads via Gmail MCP |
| Show recent Slack activity |
| Show Telegram recent chats |
| Show recent Discord channel activity (via bin/ops-discord) |
| Search Notion workspace — pages, comments, tasks |
| Parse message and contact, determine best channel, send |
| Read the specified channel or contact's messages |
| (empty) | Show channel picker menu |
Natural-language parsing: phrases like
send "deploy done" to #general on discord or to #ops-alerts on Discord should resolve to the discord branch below. Extract the channel token (the word after #, case-insensitive) and pass it as the first arg to bin/ops-discord send.
Send flow: send [message] to [contact]
send [message] to [contact]- Parse contact name and message from
.$ARGUMENTS - Determine channel by contact lookup:
- Check WhatsApp:
wacli contacts --search "[contact]" --json 2>/dev/null - Check Slack:
withmcp__claude_ai_Slack__slack_search_usersquery: "[contact]" - Check email: known from context or ask
- Check WhatsApp:
- If multiple channels found, use
:AskUserQuestion
/[WhatsApp]
/[Slack][Email] - Always preview before sending. Use
to confirm:AskUserQuestion
Ready to send via [channel]: To: [contact name] ([identifier]) Message: "[full message text]" [Send now] [Edit message] [Cancel]
If user picks "Edit message", use
AskUserQuestion with free-text to get the revised message, then re-preview.
- Send via the chosen channel. Confirm with:
Sent to [contact] via [channel] ✓
WhatsApp send
CRITICAL — READ BEFORE SENDING: Before drafting ANY WhatsApp reply, you MUST:
- Read the full conversation:
wacli messages list --chat "<JID>" --limit 20 --json - Understand which messages are from the user (
) vs the contactFromMe: true - Summarize what the conversation is about and what the contact is asking
- Only THEN draft a reply that addresses what the contact actually said
Never send a reply based on a single message. A message like "can you pull it from Klaviyo?" means nothing without knowing what "it" refers to from prior messages.
Pre-flight: Before any wacli command, check
~/.wacli/.health. If status=needs_auth or status=needs_reauth, prompt the user: "WhatsApp needs re-authentication. Run wacli auth in a separate terminal and scan the QR code, then type 'done'." Use AskUserQuestion: [Done — re-paired], [Skip WhatsApp]. On Done, restart daemon: launchctl kickstart -k gui/$(id -u)/com.claude-ops.wacli-keepalive, wait 5s.
wacli send --to "[contact]" --message "[message]"
Slack send
Use
mcp__claude_ai_Slack__slack_send_message with resolved channel/user ID.
Email send (draft)
Use
mcp__claude_ai_Gmail__create_draft — always create draft first. Then use AskUserQuestion:
Draft created for [recipient]: Subject: [subject] Body: [preview] [Send now] [Keep as draft] [Edit]
Read flow: read [channel]
read [channel]WhatsApp:
wacli chats --limit 10 --json 2>/dev/null
Show last 10 chats with sender, preview, timestamp.
Email: Use
mcp__claude_ai_Gmail__search_threads with query: "in:inbox" (NOT is:unread — scan full inbox including read messages), show thread list.
Slack: Use
mcp__claude_ai_Slack__slack_search_public_and_private with query: "in:channel" (NOT is:unread — scan full recent activity).
Telegram: Use
mcp__claude_ops_telegram__get_updates (limit: 20) and mcp__claude_ops_telegram__list_chats.
Fall back to: telegram-cli --exec "dialog_list" 2>/dev/null || echo "Telegram MCP not configured"
Discord:
${CLAUDE_PLUGIN_ROOT}/bin/ops-discord read "<CHANNEL_ID>" --limit 20 --json — requires DISCORD_BOT_TOKEN (or credential-store discord/bot-token). Fall back to bin/ops-discord channels --json if the user doesn't know the channel ID and DISCORD_GUILD_ID is set.
Notion: Use
mcp__claude_ai_Notion__notion-search with the user's query (or query: "" sorted by last_edited_time for general browsing). For each result:
- Fetch full page content with
using the page URL/ID from search resultsmcp__claude_ai_Notion__notion-fetch - Get comments with
mcp__claude_ai_Notion__notion-get-comments - Show page title, database name, last editor, and recent comments
Notion API fallback: If MCP tools fail and
NOTION_API_KEY is set, use curl -s -H "Authorization: Bearer $NOTION_API_KEY" -H "Notion-Version: 2022-06-28" -X POST https://api.notion.com/v1/search -d '{"query":"<QUERY>","page_size":10}'
Notion comment/reply
Use
mcp__claude_ai_Notion__notion-create-comment with the page ID to reply to a comment thread. For creating new pages in a database, use mcp__claude_ai_Notion__notion-create-pages.
Always preview before commenting:
Ready to comment on Notion page: Page: [page title] Comment: "[comment text]" [Post comment] [Edit] [Cancel]
Telegram send
Use
mcp__claude_ops_telegram__send_message with chat_id (from list_chats) and text.
Discord send
Shell out to
bin/ops-discord send. Three invocation shapes:
# By channel alias (resolves DISCORD_WEBHOOK_<UPPER> or DISCORD_WEBHOOK_URL) ${CLAUDE_PLUGIN_ROOT}/bin/ops-discord send "<channel-alias>" "<message>" --json # By channel snowflake (17-20 digit ID, routed through bot token) ${CLAUDE_PLUGIN_ROOT}/bin/ops-discord send "<CHANNEL_ID>" "<message>" --json # By full webhook URL (useful when the URL is stored per-project) ${CLAUDE_PLUGIN_ROOT}/bin/ops-discord send "https://discord.com/api/webhooks/<ID>/<TOKEN>" "<message>" --json
If the script exits 1 with
{"error":"no discord credential configured — run /ops:setup discord"}, prompt the user via AskUserQuestion (≤4 options per Rule 1): [Run /ops:setup discord] / [Paste webhook URL now] / [Skip]. Do NOT silently skip — that violates Rule 3.
Note:
DISCORD_WEBHOOK_URL is shared with the ops-fires notification sink (scripts/ops-notify.sh). When pre-existing, prefer it as the default for /ops:comms discord send rather than asking the user to set a separate value.
Empty arguments — channel picker
Display the header, then use batched AskUserQuestion calls (max 4 options each):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ OPS ► COMMS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Before presenting options, read
${CLAUDE_PLUGIN_DATA_DIR}/preferences.json and check which channels are configured. Only show configured channels. If <=4 total options (configured channels + "Send a message"), present in a single call. If >4, batch:
AskUserQuestion call 1 — Read channels:
[Read WhatsApp] [Read Email] [Read Slack] [More...]
AskUserQuestion call 2 (only if "More..."):
[Read Telegram] [Send a message]
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.
Execute the selected action.