Send User Message
TinyAGI/tinyagi
Send a proactive message to a paired user via their channel (Discord, Telegram, or WhatsApp).
OPS on-demand: This skill should be used when the user asks to "send a message"…
The automated check flagged lines worth reading first. See the safety section below.
$ npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-comms -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install Lifecycle-Innovations-Limited/claude-ops ops-comms --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/Lifecycle-Innovations-Limited/claude-ops.git skills-src && mkdir -p .claude/skills && cp -r skills-src/claude-ops/skills/ops-comms .claude/skills/ops-comms && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "ops-comms" agent skill from https://github.com/Lifecycle-Innovations-Limited/claude-ops/tree/main/claude-ops/skills/ops-comms into .claude/skills/ops-comms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ops-comms", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/Lifecycle-Innovations-Limited/claude-ops/tree/main/claude-ops/skills/ops-commsType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-comms -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install Lifecycle-Innovations-Limited/claude-ops ops-comms --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Lifecycle-Innovations-Limited/claude-ops.git skills-src && mkdir -p .agents/skills && cp -r skills-src/claude-ops/skills/ops-comms .agents/skills/ops-comms && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "ops-comms" agent skill from https://github.com/Lifecycle-Innovations-Limited/claude-ops/tree/main/claude-ops/skills/ops-comms into .agents/skills/ops-comms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ops-comms", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-comms -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install Lifecycle-Innovations-Limited/claude-ops ops-comms --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Lifecycle-Innovations-Limited/claude-ops.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/claude-ops/skills/ops-comms .cursor/skills/ops-comms && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "ops-comms" agent skill from https://github.com/Lifecycle-Innovations-Limited/claude-ops/tree/main/claude-ops/skills/ops-comms into .cursor/skills/ops-comms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ops-comms", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/Lifecycle-Innovations-Limited/claude-ops.git --path claude-ops/skills/ops-comms--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-comms -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install Lifecycle-Innovations-Limited/claude-ops ops-comms --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Lifecycle-Innovations-Limited/claude-ops.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/claude-ops/skills/ops-comms .gemini/skills/ops-comms && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "ops-comms" agent skill from https://github.com/Lifecycle-Innovations-Limited/claude-ops/tree/main/claude-ops/skills/ops-comms into .gemini/skills/ops-comms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ops-comms", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install Lifecycle-Innovations-Limited/claude-ops ops-commsInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-comms -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/Lifecycle-Innovations-Limited/claude-ops.git skills-src && mkdir -p .github/skills && cp -r skills-src/claude-ops/skills/ops-comms .github/skills/ops-comms && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "ops-comms" agent skill from https://github.com/Lifecycle-Innovations-Limited/claude-ops/tree/main/claude-ops/skills/ops-comms into .github/skills/ops-comms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ops-comms", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-comms -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install Lifecycle-Innovations-Limited/claude-ops ops-comms --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Lifecycle-Innovations-Limited/claude-ops.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/claude-ops/skills/ops-comms .opencode/skills/ops-comms && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "ops-comms" agent skill from https://github.com/Lifecycle-Innovations-Limited/claude-ops/tree/main/claude-ops/skills/ops-comms into .opencode/skills/ops-comms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ops-comms", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
ops-commsOPS on-demand: This skill should be used when the user asks to "send a message"…
Ops Comms is an agent skill from Lifecycle-Innovations-Limited/claude-ops. OPS on-demand: This skill should be used when the user asks to "send a message"…
Its SKILL.md is about 6.4k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files, including reference files (for example `references/cli.md`).
It sits in Productivity & Automation. It works with WhatsApp. The repository describes itself as: Business operating system for Claude Code — 57 skills, 21 agents, smart daemon. Unified inbox (WhatsApp/Email/Slack/Telegram), autonomous PR merge, full-AWS monitoring, revenue… The licence is MIT.
3 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 1aa0928. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
BashReadGrepGlobSkillAskUserQuestionmcp__claude_ai_Gmail__search_threadsmcp__claude_ai_Gmail__get_threadmcp__claude_ai_Gmail__create_draftmcp__claude_ai_Slack__slack_send_message…and 12 more on the same allowed-tools line.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
curlFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
api.notion.comdiscord.comFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
CLAIM_KEYNOTION_API_KEYDISCORD_BOT_TOKENFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Ops Comms loads about 6.4k tokens when it runs, and up to ~7.2k if it reads all its reference files. Until then it costs about 23 tokens; SKILL.md has 2,831 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found patterns that need a careful read before installing.
GIN_ROOT}/bin/ops-discord send "https://discord.com/api/webhooks/<ID>/<TOKEN>" "<message>" --jsonallowed-tools: Bash, Read, Grep, Glob, Skill, AskUserQuestion, mcp__claude_ai_Gmail__search_threads, mcp__claude_aiAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from Lifecycle-Innovations-Limited/claude-ops at commit 1aa0928, republished under its MIT licence (© Lifecycle-Innovations-Limited). 2,831 words, ~6,419 tokens.
.claude/skills/ops-comms/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Load ops-rules before acting. Public repo (no personal data). Outbound: one draft → one approval → one send. If AskUserQuestion / Workflow are missing, follow Rule 10 in ops-rules (Hermes: numbered options / two-turn Telegram card; delegate_task).
Before executing, load available context:
Daemon health: Read ${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/daemon-health.json
lsof -i :8080 | grep LISTENlaunchctl kickstart -k gui/$UID/com.${USER}.whatsapp-bridgeOps memories: Before drafting any message, check ${CLAUDE_PLUGIN_DATA_DIR}/memories/:
contact_*.md — load profile for the recipientpreferences.md — match user's communication style, language, and tonedonts.md — restrictions that must not appear in any draftPreferences: Read ${CLAUDE_PLUGIN_DATA_DIR}/preferences.json for default_channels to determine which channel to prefer when multiple are available for a contact.
| Pattern | Action |
|---|---|
whatsapp | Show WhatsApp recent chats — offer to read or send |
email | Show recent email threads via Gmail MCP |
slack | Show recent Slack activity |
telegram | Show Telegram recent chats |
discord | Show recent Discord channel activity (via bin/ops-discord) |
notion | Search Notion workspace — pages, comments, tasks |
voice | Voice / phone / video — routes to /ops:ops-voice (bin/ops-voice) |
call * | Native Phone.app call via bin/ops-voice phone |
facetime * | FaceTime audio/video via bin/ops-voice facetime |
zoom | Start an instant Zoom meeting via bin/ops-voice zoom start |
send * to * | Parse message and contact, determine best channel, send |
read * | Read the specified channel or contact's messages |
home alarm | Pipe a Homey alarm event as a WhatsApp/Telegram alert — delegates to /ops:ops-home alarm --notify (only if home_automation is configured in $PREFS_PATH) |
| (empty) | Show channel picker menu |
Natural-language parsing:
send "deploy done" to #general on discord → bin/ops-discord send general "deploy done".call <contact> / dial <contact> / phone <contact> → resolve number, then bin/ops-voice phone <E.164>.facetime <contact> (with optional audio) → bin/ops-voice facetime <handle> [--audio].start a zoom / new zoom meeting → bin/ops-voice zoom start.join zoom <ID> → bin/ops-voice zoom join <ID>.text <contact> "<body>" / sms <contact> "<body>" → bin/ops-voice twilio-sms <to> $TWILIO_FROM_NUMBER "<body>" (guarded by per-message approval).send [message] to [contact]$ARGUMENTS.mcp__whatsapp__search_contacts {query: "[contact]"} 2>/dev/nullmcp__claude_ai_Slack__slack_search_users with query: "[contact]"AskUserQuestion: [WhatsApp] / [Slack] / [Email]AskUserQuestion to confirm: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.
Sent to [contact] via [channel] ✓CRITICAL — READ BEFORE SENDING: Before drafting ANY WhatsApp reply, you MUST:
mcp__whatsapp__list_messages {chat_jid: "<JID>", limit: 20}is_from_me: true (user sent) vs is_from_me: false (contact sent)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: Check bridge is running: lsof -i :8080 | grep LISTEN. If not running, restart: launchctl kickstart -k gui/$(id -u)/com.${USER}.whatsapp-bridge and wait 5s.
mcp__whatsapp__send_message {recipient: "[contact_jid]", message: "[message]"}Use mcp__claude_ai_Slack__slack_send_message with resolved channel/user ID.
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]Before drafting to a person, load relations for their brief, open
commitments and the full-context draft order, and vip for their tier. A
tier-1 recipient is drafted with extra care and never handled from a digest
line. Neither skill can send; Rule 6 still owns the send.
Rule 6 governs whether a message may go out. This section governs whether the message is right. Every item below is a failure mode seen on a real run, where the approval gate held and the content was still wrong.
Never state a fact in someone else's inbox from memory. A price, a date, an availability, a "who currently holds X" all get a cheap check first: a search, the actual thread, the calendar, the invoice.
Claims about the recipient's own access get probed, not inferred. Telling a colleague their access is broken is a claim about a system you can inspect. A wrong diagnosis sends a competent person chasing a non-problem, and they will believe you because you appear to hold the admin view. Sort each access item into fixed, never broken, or theirs, and say which. Telling someone "that already works" stops them building a workaround for a permission they already have.
The hardest fabrications are not invented facts, they are invented relationships between two real things that share a name, an entity, or a keyword. Each half is true, the sentence joining them is not, and it reads as diligence rather than a guess.
"X should be coordinated with Y" is itself a factual claim and needs its own evidence. A company or person appearing in two matters is weak evidence they are the same workstream and often strong evidence of a naming coincidence. Be especially suspicious of an advisory clause nobody asked for: the recipient asked a yes or no question, and a "let's just align with..." rider is exactly where invented linkage hides. When challenged on such a clause, probe and delete it rather than rewording it into something defensible.
An inbound message that mentions a task is not automatically a task for the user. When the sender owns the workstream, a heads-up about their own work in progress is not an instruction transferring that work.
Before staging any reply containing a first-person commitment ("I'll pick that up", "I'll clean that up"), check whose function it is, who has the access, and whether the commitment is even wanted. A message that only needs acknowledging gets acknowledgement. Volunteering labour reads as helpful and creates real obligations nobody tracks. When the work does need doing and the counterparty owns it, name it and hand it back rather than quietly taking it on.
A narrow A/B to the user is right for a real trade-off and wrong when the counterparty already supplied the answer inside the message being triaged. An identifier a person gives for their own account (login, email, phone, handle, bank account) is authoritative. A conflict with the admin view means the estate is misconfigured, not that they misremembered their own account. Use the probing to shape the message, not to defer. Escalate only when the action is irreversible and expensive; two systems disagreeing is neither.
The tell that this rule was broken is a closing question to the user whose answer is already quoted verbatim in the inbound message.
A queue of drafts is researched at one moment and sent over hours. Any draft referencing a pending external process (a bank instruction, a signature, a shipment, a filing) must have that process re-checked immediately before the send, not just at research time. A draft that correctly said "still waiting, no news" became wrong when the counterparty's reply landed mid-queue.
Two consequences are easy to miss: the draft's central claim can flip outright, and new inbound often creates a second outbound that also needs drafting. Search results encountered mid-queue get triaged, not skimmed past.
A parallel session may have already sent it. Multiple agents can work the same inbox and send under the same identity. Treat any outbound from the user's own identity that this session did not write as another agent's work: verify its claims at the source, report the item closed with that evidence, and move on. Do not re-send, and do not assume it was correct just because it exists.
When someone replies "which of these did you mean?" or summarises your position back before proceeding, do not pick the more plausible reading. Open what the user actually sent and read their own words. A partner who guesses wrong and proceeds on that guess can commit the user, in writing, to a different deal than the one they proposed. Their politeness is not evidence; a soft hedge from a competent counterparty is exactly where an unnoticed inversion lives.
Low-confidence pushback ("check, I think we already signed", "I might have credentials too") is a hard stop and a research instruction, not a hedge to be reassured about. Re-probe the primary source and come back with what it says, including "partly, the mechanism is X not Y". Never defend a draft against the user's own recollection without checking first.
A subagent audit is a research artifact, not a finding. Before acting on a cancel-or-delete verdict, open one paid invoice and read the SKU, quantity, billing address, and payment history. "No API key in the environment" does not mean "unused": no-code SaaS runs entirely in a web UI. A "paused" or free-tier notice may belong to an entirely different account than the paid plan.
When the user says "just reply", answer only the inbound ask. Do not bolt on extra forms, address updates, or multi-item checklists they did not ask for in that draft. Extra tracks get a separate message after the send if still needed. When the user corrects a draft to be shorter or more spoken, rewrite against their wording, not a re-packaged version of your own.
When a message tells someone what to work on, do not paraphrase the tracker. Re-run the query and enumerate every open item in board order with identifier, priority, and state. Vagueness is how items get silently dropped and how the recipient invents their own ordering. If something is deliberately excluded, say so and why rather than omitting it silently.
When an action destroys an asset (a contact list, a mailbox, a dataset), order the work export, then verify the export is real, then destroy. "Verify" means row count and headers inspected against an expectation stated up front, so the check is falsifiable. An export returning a few hundred rows from a plan holding tens of thousands is a failed export that looks like a successful one. Split it across two approvals: the export can run autonomously, the destroy waits for the user.
Do not treat a send tool returning without an error as delivery. Confirm on the
channel: the outbound row appears in the thread, or the message carries the
SENT label. A DRAFT is not a delivery, and a broken sender alias can bounce
seconds later. If verification fails, do not archive; an unverified send is an
unanswered thread.
Every thread you reply to gets a disposition immediately. There is no "leave it and see".
Only threads genuinely waiting on the user's own next action stay in the inbox.
After one item is approved, sent, verified, and dispositioned, stage the next queue item automatically without waiting for "go" or "next". Stop only for the per-draft approval itself, a real decision that is the user's to make, or a blocker only a human can clear.
read [channel]WhatsApp:
mcp__whatsapp__list_chats {sort_by: "last_active"}Show last 10 chats with sender, preview, timestamp. Use mcp__whatsapp__list_messages {chat_jid, limit: 5} to preview each chat.
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 (multi-workspace):
Read the derived channels.slack.workspaces[] object from the pre-gathered bin/ops-unread output (NOT the raw preferences.json → slack_workspaces[], which has no available field — it only persists workspace metadata). The bin/ops-unread step resolves each workspace's token_env and emits available: true|false per entry. Iterate that array:
available: true entry, use mcp__claude_ai_Slack__slack_search_public_and_private with query: "in:channel" (NOT is:unread) if the MCP token matches, or direct curl for non-bound workspaces. To resolve the token for direct curl, the entry's token_env field is the name of the env var; validate it matches ^[A-Za-z_][A-Za-z0-9_]*$ before indirect expansion (${!token_env}) to avoid bash aborting on invalid identifiers.Slack/<workspace_a>, Slack/<workspace_b>, etc.channels.slack.multi_workspace == false / legacy mode: fall back to mcp__claude_ai_Slack__slack_search_public_and_private if channels.slack.available == true, otherwise report "Slack not configured".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:
mcp__claude_ai_Notion__notion-fetch using the page URL/ID from search resultsmcp__claude_ai_Notion__notion-get-commentsNotion 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}'
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]Use mcp__claude_ops_telegram__send_message with chat_id (from list_chats) and text.
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>" --jsonIf 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.
All voice traffic flows through bin/ops-voice (full surface documented in the ops-voice skill). Native channels (Phone.app, FaceTime, Zoom start|join) need no credentials; programmatic channels (Twilio voice/SMS, Bland AI, Zoom schedule) follow the standard credential-resolution order.
# Native — no creds
${CLAUDE_PLUGIN_ROOT}/bin/ops-voice phone "+1234567890" --json
${CLAUDE_PLUGIN_ROOT}/bin/ops-voice facetime user@example.com --audio --json
${CLAUDE_PLUGIN_ROOT}/bin/ops-voice zoom start
${CLAUDE_PLUGIN_ROOT}/bin/ops-voice zoom join 1234567890 --pwd <password>
# Programmatic — gated by Rule 6 (per-message approval)
${CLAUDE_PLUGIN_ROOT}/bin/ops-voice twilio-call "+1234567890" "$TWILIO_FROM_NUMBER" --twiml "<URL>" --json
${CLAUDE_PLUGIN_ROOT}/bin/ops-voice twilio-sms "+1234567890" "$TWILIO_FROM_NUMBER" "<body>" --json
${CLAUDE_PLUGIN_ROOT}/bin/ops-voice bland-call "+1234567890" "<task prompt>" --jsonSend-flow integration: when $ARGUMENTS looks like call <contact>, facetime <contact>, text <contact> "<body>", or have an AI call <contact> and ...:
mcp__whatsapp__search_contacts or preferences.json → contacts).phone, facetime, zoom): preview [Place call via <channel> to <contact>] [Cancel] then invoke.AskUserQuestion per message (Rule 6). Never batch.How the approval is actually enforced — see scripts/outbound-guard/README.md. One
store, /tmp/.claude-outbound-guard.json, shared by every CLI. The owner arms it from
their own shell with ! ok (1 message), ! ok 3, or ! ok all (10, capped). A message
is identified by recipient plus content, so the same message crossing the PreToolUse
hook and the MCP proxy costs one unit rather than two. A counter does not replace Rule 6:
you still stage each draft and wait for a yes.
Three traps that have each caused a real bypass:
SENT label is not delivery. A send-as alias with bad SMTP credentials is
stamped SENT and bounced by mailer-daemon seconds later in the same thread. Check
for the bounce before reporting a message as delivered.mcp__whatsapp-<label>__send_message; an exact-string allowlist silently stops
covering them while still appearing to run (Rule 8).AskUserQuestion with [Run /ops:ops-voice setup] / [Paste credential now] / [Try native instead] / [Skip] (Rule 3 — never silently skip).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]
[Make a call (voice)]
[Send a message]If all channels are configured, that's 6+ options — always batch. If only 3 channels are configured, "Read X" + "Read Y" + "Read Z" + "Send a message" = 4, fits in one call. The voice channel is configured iff preferences.json → channels.voice is present OR default_channels contains "voice".
Execute the selected action.
CLAIM_KEY by channel and message unit:
slack:thread:<channel>:<ts>slack:thread:wa:<jid>:<ts> (reuse slack: namespace for threads)comms:draft:<channel>:<YYYY-MM-DDTHH-MM>CLAIM_KEY="slack:thread:<channel>:<ts>" # adjust per channel
ledger query --claim-key "$CLAIM_KEY" --since=-PT24HSkip any message/thread where a done or in_progress entry exists. Surface
awaiting_user entries as "draft already staged — resend or edit?"
# Claim before drafting
ledger write \
--claim-key "$CLAIM_KEY" \
--kind "draft" \
--status "in_progress" \
--title "Comms: <channel> — <brief description>" \
--ttl-sec 3600
# After user approves + send fires
ledger write \
--claim-key "$CLAIM_KEY" \
--kind "send" \
--status "done" \
--title "Comms: <channel> — <brief description>" \
--context "sent via <channel>"CLI detail: references/cli.md.
© Lifecycle-Innovations-Limited, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file (references) in claude-ops/skills/ops-comms of Lifecycle-Innovations-Limited/claude-ops.
Open the folder on GitHubat commit 1aa0928
Ops Comms next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Ops Comms this skillLifecycle-Innovations-Limited/claude-ops | 542 | — | ~6.4k | Automated safety check: Warn | MIT | |
| Send User MessageTinyAGI/tinyagi | 3.6k | — | ~829 | Automated safety check: Pass | MIT | |
| MuseKevPH2026/muse-catch | 128 | — | ~1.6k | Automated safety check: Notes | AGPL-3.0 | |
| Executive Digestmgonto/executive-assistant-skills | 118 | — | ~2.6k | Automated safety check: Notes | None | |
| Ak Dev New Messaging Integrationyaalalabs/agent-kernel | 192 | — | ~4.6k | Automated safety check: Pass | Apache-2.0 | |
| Add Telegramsbusso/claudeclaw | 194 | — | ~1.7k | Automated safety check: Notes | MIT |
TinyAGI/tinyagi
Send a proactive message to a paired user via their channel (Discord, Telegram, or WhatsApp).
KevPH2026/muse-catch
Muse · Catch — AI 灵感捕手。浏览器插件 + 任意 Agent(Telegram/WhatsApp/Discord…)一键捕获灵感,AI自动提炼,Web Dashboard浏览。分级LLM路由:Agent内置LLM隐私分析 + TokenRouter云端分布式调用。当用户说「灵感」「捕获」「Muse」「Catch」「记下来」「收藏」「稍后读」时自动触发。
mgonto/executive-assistant-skills
Generate the daily executive digest — a single WhatsApp summary of everything needing attention: stalled scheduling, pending intros, unanswered emails, promised follow-ups, open Todoist tasks, and…
yaalalabs/agent-kernel
Step-by-step guide for adding a new messaging platform integration to Agent Kernel.
sbusso/claudeclaw
Add Telegram as a channel. An agent skill from sbusso/claudeclaw.
davepoon/buildwithclaude
Automate WhatsApp Business tasks via Rube MCP (Composio): send messages, manage templates, upload media, and handle contacts.
Lifecycle-Innovations-Limited/claude-ops
OPS on-demand: This skill should be used when the user asks to "ops dashboard", "pixel HQ", or…
Lifecycle-Innovations-Limited/claude-ops
OPS on-demand: This skill should be used when the user asks to "go to market", "GTM plan", or…
Lifecycle-Innovations-Limited/claude-ops
OPS on-demand: This skill should be used when the user asks to "klaviyo", "ads spend", or…
Lifecycle-Innovations-Limited/claude-ops
OPS on-demand: This skill should be used when the user asks to "tweet", "post to linkedin", or…
Lifecycle-Innovations-Limited/claude-ops
OPS on-demand: This skill should be used when the user asks to "yolo mode", "run the business today"…
Lifecycle-Innovations-Limited/claude-ops
OPS on-demand: This skill should be used when the user asks to "datadog", "APM alerts", or…
Works with
Categories
OPS on-demand: This skill should be used when the user asks to "send a message"…. Ops Comms is an agent skill from Lifecycle-Innovations-Limited/claude-ops.
Ops Comms fits situations like: asks to send a message….
Run `npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-comms -a claude-code`. Or copy the skill folder (claude-ops/skills/ops-comms in Lifecycle-Innovations-Limited/claude-ops) into .claude/skills/ops-comms in your project. Claude Code loads it when a task matches its description.
Run `npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-comms -a codex`. Or copy the skill folder (claude-ops/skills/ops-comms in Lifecycle-Innovations-Limited/claude-ops) into .agents/skills/ops-comms in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-comms -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/ops-comms, .gemini/skills/ops-comms, .github/skills/ops-comms and .opencode/skills/ops-comms in your project.
Going by SKILL.md and its folder, Ops Comms needs the command-line tools its instructions call (curl) and credentials named CLAIM_KEY, NOTION_API_KEY and DISCORD_BOT_TOKEN. Its frontmatter pre-approves these tools: Bash, Read, Grep, Glob, Skill, AskUserQuestion, mcp__claude_ai_Gmail__search_threads, mcp__claude_ai_Gmail__get_thread, mcp__claude_ai_Gmail__create_draft, mcp__claude_ai_Slack__slack_send_message, mcp__claude_ai_Slack__slack_read_channel, mcp__claude_ai_Slack__slack_search_users, mcp__claude_ai_Slack__slack_search_public_and_private, mcp__claude_ops_telegram__send_message, mcp__claude_ops_telegram__get_updates, mcp__claude_ops_telegram__list_chats, mcp__claude_ai_Notion__notion-search, mcp__claude_ai_Notion__notion-fetch, mcp__claude_ai_Notion__notion-get-comments, mcp__claude_ai_Notion__notion-create-comment, mcp__claude_ai_Notion__notion-update-page, mcp__claude_ai_Notion__notion-create-pages.
SKILL.md names 2 domains. In commands or code: api.notion.com and discord.com; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md flagged 1 warning(s): mentions a paste, webhook or tunnelling service often used to send data out. Read the flagged lines before installing; the check is not a guarantee either way.
Ops Comms is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.4k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 749 tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Ops Comms: Send User Message (TinyAGI/tinyagi, 3.6k stars), Muse (KevPH2026/muse-catch, 128 stars), Executive Digest (mgonto/executive-assistant-skills, 118 stars) and Ak Dev New Messaging Integration (yaalalabs/agent-kernel, 192 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Lifecycle-Innovations-Limited (a GitHub organization) maintains it in Lifecycle-Innovations-Limited/claude-ops, which has 542 GitHub stars. The repository holds 67 skills in this directory. The repository was last updated on October 9, 2026.
Source: Lifecycle-Innovations-Limited/claude-ops on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.