ops-inbox

GitHub

自动化处理OPS收件箱,支持WhatsApp等渠道。通过后台扫描、自动起草草稿及30秒心跳机制实现快速响应与发送审批流程,确保父会话持续活跃。

claude-ops/skills/ops-inbox/SKILL.md Lifecycle-Innovations-Limited/claude-ops

Trigger Scenarios

检查收件箱 收件箱清零

Install

npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-inbox -g -y
More Options

Non-standard path

npx skills add https://github.com/Lifecycle-Innovations-Limited/claude-ops/tree/main/claude-ops/skills/ops-inbox -g -y

Use without installing

npx skills use Lifecycle-Innovations-Limited/claude-ops@ops-inbox

指定 Agent (Claude Code)

npx skills add Lifecycle-Innovations-Limited/claude-ops --skill ops-inbox -a claude-code -g -y

安装 repo 全部 skill

npx skills add Lifecycle-Innovations-Limited/claude-ops --all -g -y

预览 repo 内 skill

npx skills add Lifecycle-Innovations-Limited/claude-ops --list

SKILL.md

Frontmatter
{
    "name": "ops-inbox",
    "effort": "high",
    "maxTurns": 60,
    "description": "OPS on-demand: This skill should be used when the user asks to \"check inbox\", \"inbox zero\"…",
    "allowed-tools": [
        "Bash",
        "Read",
        "Grep",
        "Glob",
        "Skill",
        "Agent",
        "AskUserQuestion",
        "TeamCreate",
        "SendMessage",
        "TaskCreate",
        "TaskUpdate",
        "TaskList",
        "CronCreate",
        "CronList",
        "mcp__gog__gmail_search",
        "mcp__gog__gmail_read_thread",
        "mcp__gog__gmail_send",
        "mcp__gog__gmail_labels",
        "mcp__claude_ai_Slack__slack_search_public_and_private",
        "mcp__claude_ai_Slack__slack_read_channel",
        "mcp__claude_ai_Slack__slack_list_channels",
        "mcp__claude_ai_Slack__channels_list",
        "mcp__claude_ai_Notion__notion-search",
        "mcp__claude_ai_Notion__notion-fetch",
        "mcp__claude_ai_Notion__notion-query-data-sources",
        "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",
        "mcp__whatsapp__list_chats",
        "mcp__whatsapp__list_messages",
        "mcp__whatsapp__search_contacts",
        "mcp__whatsapp__send_message",
        "mcp__whatsapp__get_chat",
        "mcp__whatsapp__get_message_context",
        "mcp__whatsapp__archive_chat",
        "mcp__whatsapp__resync_app_state",
        "mcp__plugin_imessage_imessage__chat_messages",
        "mcp__plugin_imessage_imessage__reply"
    ],
    "argument-hint": "[channel: whatsapp|imessage|email|slack|telegram|discord|notion|all]"
}

OPS ► INBOX ZERO

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).

Parent session: AUTO-START DRAFTING

This skill MUST run in the parent session. Never set context: fork — that parks the whole run (including drafts) in a background agent, so the parent idles until the owner types "continue drafting". Measured 2026-09-21.

Always report to the main agent by default. Every Agent / Workflow / forked worker this skill launches returns KEEP rows, thread arcs, and draft text to the parent. It never AskUserQuestion, never numbered-options the owner, never waits for "continue". If this skill itself was spawned as a subagent, the same rule: the last message is a report for the parent, not a card for the owner.

30-second parent heartbeat (default). At the start of every run, launch:

"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-parent-heartbeat.sh"

via Bash run_in_background: true. Default interval is 30 seconds (OPS_INBOX_HEARTBEAT_SEC). The job sleeps, prints one INBOX HEARTBEAT line, and exits. The job's exit IS the ping to this parent session. When it fires: print that line to the owner, then relaunch the same command. Never wait for the owner to ask for a status.

Every background worker also SendMessages the parent at least every 30 seconds with one line (channel=… keep=… drafting=…). That is the default, not an opt-in.

On /ops:ops-inbox / /ops-inbox, start drafting in THIS session without a second prompt:

  1. Kick the cheap scan (ops-inbox-scan) and fan-out readers immediately.
  2. The moment the first KEEP / NEEDS_REPLY exists (scan JSON, keep.md, thread_tail, or a reader returning), stage that draft. Do not wait for the rest of the inbox.
  3. Queue further drafts as readers land. One draft → one approval → one send.
  4. Never wait for "continue drafting", "go", or "next?". The slash command is the start signal.

Scanners stay background and read-only. Sends never leave the parent.

⚠️ WHATSAPP TRANSPORT — MCP ONLY, NEVER wacli

For all WhatsApp operations in this skill (list chats, read messages, search contacts, send replies, archive chats), use the mcp__whatsapp__* tool family backed by the whatsmeow (Go) whatsapp-bridge — upstream lharries/whatsapp-mcp. (Earlier docs misnamed this as "Baileys" — Baileys is the Node.js WhatsApp library; this bridge uses go.mau.fi/whatsmeow.)

Server name. mcp__whatsapp__* is the single-account default. Installs with more than one account register one server per account (whatsapp-nl, whatsapp-us, whatsapp-personal, whatsapp-work, ...) and have no plain mcp__whatsapp__* at all. whatsapp-cos is not an account — do not send on it. Resolve the real name from the available tools before the first call, and when several accounts exist, scan each one separately and keep the results labelled by account. See CLAUDE.md / ops-rules Rule 8.

NEVER call the legacy wacli CLI (wacli chats list, wacli messages list, wacli send, wacli doctor, wacli history backfill, etc). The wacli store and keepalive daemon are deprecated for this skill.

If you find yourself reaching for any wacli ... shell command, stop and use the MCP tool with the same intent:

Intent ✅ Use this ❌ Do NOT use
List recent chats mcp__whatsapp__list_chats {sort_by: "last_active", limit: 25} wacli chats list
Read full thread mcp__whatsapp__list_messages {chat_jid, limit: 20} wacli messages list
Full-text search mcp__whatsapp__list_messages {query: "<text>", limit: 20} wacli messages search
Resolve a contact mcp__whatsapp__search_contacts {query: "<name>"} wacli contacts
Send a reply (after approval) mcp__whatsapp__send_message {recipient: "<JID>", message: "<text>"} wacli send
Health check ops-wa-accounts --list, then curl -s -o /dev/null -w "%{http_code}" "$WA_API/api/v1/status" (401 is healthy when the route is Bearer-gated). Never lsof :8080, never launchctl kickstart unless policy says the bridge is local. wacli doctor / ~/.wacli/.health
Trigger history backfill curl -fsS -X POST "$WA_API/api/backfill" (resolve $WA_API first — see "WHICH NUMBER" below; claude-ops patch — runs per-chat against the 50 most-recent chats; bridge also auto-backfills 5s after every Connected event)

Rationale: the bridge exposes a typed MCP surface, returns consistent JSON shapes (is_from_me, content, timestamp, sender), supports FTS5 search natively, and avoids store-lock contention with the wacli keepalive daemon. Mixing the two surfaces caused inconsistent state in past sessions.

Sole exception: the ~/.wacli/.health file is still readable for legacy daemon-health surfacing in other skills, but no wacli command should be invoked from this skill.

⚠️ WHICH NUMBER — resolve the account before the first read or send

This box may run more than one WhatsApp account. The bridge process may be local, or it may run on another host with this machine as a client. Clients dial a local reverse-proxy URL from policy (api / bridge_port in $PREFS_PATH .channels.whatsapp, written by /ops:setup step 3b; $OPS_DATA_DIR/registry.json .whatsapp is the same shape). They never dial a remote IP, never guess :8080, and never start a local LaunchAgent "to recover" when policy says the bridge is elsewhere. A 401 on $WA_API/api/v1/status is healthy when that route is Bearer-gated.

Resolve first, every run, before any read, archive, or send:

"$CLAUDE_PLUGIN_ROOT/bin/ops-wa-accounts" --list      # every account: port, number, agent, api
# Two agent-enabled accounts: scan EACH, labelled. Do not call --port (exits 3 on purpose).
# For each agent_enabled account in the JSON: WA_API=$account.api

ops-wa-accounts reads local whatsapp-bridge* stores if present, then overlays policy from $PREFS_PATH (wins), then registry, then legacy ~/.config/whatsapp/agent-policy.json. Policy api / bridge_port always wins over a leftover local store's launcher port. A client box with no messages.db is still resolved. It exits 3 rather than pick one account when more than one is agent-enabled — that is a loop, not a stop.

The rules:

  1. Never hardcode a port, never use whichever port answers, never probe a remote IP. Use each account's api from the resolver JSON.
  2. A number can be agent-disabled. An account with agent_enabled: false is off-limits: do not scan it, do not archive it, do not send from it, do not pair or re-pair it.
  3. Two agent-enabled accounts is not ambiguous. Scan each, keep results labelled by account, reply on the number the thread lives on. Stop and ask only when none are enabled, or when unpolicy'd bridges appear with no api.
  4. Reply on the number the thread lives on. Cross-account replies are never an acceptable fallback.
  5. Rule 8 applies to the MCP surface too. With several accounts the servers are named per account (mcp__whatsapp-nl__*, mcp__whatsapp-us__*, or whatsapp-personal / whatsapp-work) and a bare mcp__whatsapp__* / mcp__whatsapp-cos__* is not an account. Match the registered name; do not assume. Hermes wa status / wa thread / wa find is a valid read path on a client box when MCP is down.

Why this section exists: a full inbox run once followed this skill's hardcoded 127.0.0.1:8080 and sent every reply from the wrong number. Guessing a port, or treating a missing local messages.db as "WhatsApp down", repeats that class of failure.

Runtime Context

Default: fan out one read-only scanner per configured channel after the offline scan. Skip fan-out only in the trivial case (~1–3 candidates). Full freshness / Mac fallback / version-heal / watcher: references/runtime.md.

Every run, in order:

  1. Rank by VIP first. Load the vip skill and resolve the VIP set once (/ops:vip list). Tier-1 senders are read and answered before anything else, tier-2 next, everything else after. A VIP with an unanswered inbound is surfaced individually with thread context, never as a line in a digest. Before drafting to any person, load relations for their brief and open commitments.
  2. Resolve WhatsApp accounts (ops-wa-accounts — never hardcode a port). Scan every agent-enabled number.
  3. Freshness: ~/bin/wa-inbox-fresh.sh (blocking, bounded). Then bin/ops-inbox-scan (defaults to every enabled account, --debt-days 90). Never an unread listing: unread is a display state and is empty for every thread already opened on a phone. Forgotten unanswered asks (archived/read, still their ball, inside 90 days) come back tagged forgotten — on WhatsApp AND on email, where a Gmail filter or category can hide a live thread from in:inbox entirely. 2b. Every other channel in the same pass, in parallel: iMessage/SMS ($HERMES_HOME/bin/imessage-inbox-scan --days 30 — copies -wal/-shm first or recent messages are invisible; alphanumeric shortcodes and OTP bodies are fyi, never needs_reply). Slack: channels_me {channel_types:"im,mpim"} then conversations_history per human DM, never conversations_unreads — unread is never a filter (Sam, 2026-09-17), a DM he read on his phone and never answered stays invisible to an unread listing. Telegram user dialogs if configured. Email is already in the scan. A miss on one channel is not absence on another.
  4. All-context sweep (Rule 9 + references/details.md "ALL CONTEXT SOURCES"): query every configured calendar, mailbox, and messaging channel before any schedule claim or NEEDS_REPLY draft. Google Calendar alone is not enough — Notion show/calendar databases count when Notion is configured.
  5. bin/ops-inbox-archive-set report-only. Present KEEP vs ARCHIVE; --apply only after explicit OK.
  6. Deep-read KEEP / NEEDS_REPLY. Fan out if volume (references/fan-out.md). One read-only worker per channel, then one per KEEP thread-chunk. Stage the first draft as soon as one KEEP exists — do not wait for the full scan, and do not wait for "continue drafting". Stage the next draft the moment the previous card is answered — no "next?" pause.
  7. Stage drafts one at a time (Rule 6) in this parent session. Archive after a verified send.

Every suggested send carries reasoning

A draft without this block is incomplete. Never show only the outbound text.

Print, in the same bubble as the exact draft, three short lines in plain words:

  • Why this reply: what in the live thread this answers (quote the ask, not a vibe).
  • Expected outcome: what happens after they read it (they stop chasing, they can act, the thread closes).
  • Why that is good: the concrete gain.

If you cannot fill all three from the thread, do not stage. Go read more.

Channel processing, FULL-THREAD AWARENESS GATE, and per-channel recipes: references/details.md.

Standing behavior: RUN WIDE — parallel subagents / agent-teams / workflow by DEFAULT

Every /ops:ops-inbox run should be fast for the owner, whose time is spent only reviewing and approving — never waiting on serial reads. By default:

  • Fan out the moment there is more than a trivial amount of work. After the offline ops-inbox-scan first pass, push the per-thread deep-read / dedup / context-gathering / draft-writing into parallel background workers — Workflow fan-out (preferred) or an Agent-Teams read-only scanner per channel/thread-chunk. Do NOT deep-read dozens of threads serially in the main session.
  • Do research, context-gathering, and draft-writing in the background while the owner works. Kick off the readers/drafters; let them build full-thread arcs, cross-channel dedup, contact profiles, and staged draft text concurrently. Surface results as they land so the owner approves in a steady stream instead of after one big serial pass.
  • Parallelism NEVER changes the safety model. Workers are strictly READ-ONLY — they classify and return draft text only. Every outbound send stays in the main session, one draft → one AskUserQuestion → one approval → one send (Rule 6 + PER-DRAFT APPROVAL).
  • Respect the box concurrency ceiling (heartbeat MAX_BUSY) — queue extra work rather than exceeding it.

Scan engine — offline script triages first, Workflow fan-out is the DEFAULT for deep per-thread work

Run bin/ops-inbox-scan FIRST. It is the primary scan engine. It classifies the two heaviest channels — WhatsApp (direct read of the whatsmeow sqlite store) and Email (one gog gmail search) — deterministically, in-process, in well under a second, emitting compact JSON. No subagents, no MCP, near-zero tokens.

"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-scan" --all-accounts --pretty   # EVERY enabled number
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-scan" --whatsapp-only     # WA only
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-scan" --days 14           # wider window
# target one specific WhatsApp account instead:
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-scan" \
  --wa-store ~/.local/share/whatsapp-mcp/whatsapp-bridge-<label>/store/messages.db \
  --bridge-port <port>

--all-accounts is the default choice when more than one number is agent-enabled. It scans each one, labels every row with its account and bridge port, and still emits the flat whatsapp buckets older consumers read. Without it the scan exits 3 rather than pick a number, and a caller that "handles" that by scanning one account silently reports half an inbox.

NEVER substitute an unread listing for a scan. unread is a DISPLAY state: an owner who reads on a phone leaves every real thread at zero unread, so an unread-only sweep reports inbox zero over a mailbox full of unanswered asks. The working set is what has not been DEALT WITH (handled=0, falling back to archived=0), never what has not been seen.

AND NEVER SUBSTITUTE A LABEL FOR A CONVERSATION — measured 2026-09-16, email. The same defect has a second shape: in:inbox is a LABEL, not a state of the conversation. A Gmail filter, a category (Promotions/Updates/Social/Forums), a mute, or one stray archive strips INBOX from a live thread, and an inbox-only query then reports the mailbox clean while a counterparty waits. Real miss: Salim Jawad at Believe delivered the finished UGC fan videos on the MRT Gen AI Videos thread; Gmail had filed that thread out of the inbox, so three consecutive scans said email was handled and nobody answered him. ops-inbox-scan now runs a second email pass (--debt-days, default 90) over archived, non-promo, human mail and reopens every thread whose NEWEST message is inbound and never answered; those rows arrive in email.needs_reply tagged forgotten: true with a forgotten_count and a note. The rule to carry into any channel you add: debt is DIRECTION, not label or unread state. The honest test is always "whose message is last in this thread, and did we answer after it" — never "is it still in the inbox".

Unread is never a filter, on any channel (Sam, 2026-09-17). A message Sam opened and navigated away from without replying still owes a reply — read state is a display flag, not a proxy for "handled". conversations_unreads only sees what Sam has not opened; a DM he read on his phone and never answered is invisible to it. Never use it, and never use whatsapp_unread, to decide what needs triage. Always confirm direction with conversations_history (Slack) or whatsapp_find / whatsapp_thread (WhatsApp) — who spoke last, not what is marked unread. A guard (unread-filter-guard.py, wired into pre-tool-dispatcher.py on hub/Mac/HYPEST) hard-blocks whatsapp_unread, conversations_unreads, and any is:unread / unread_count / unread=1 filter in a terminal command.

ON SAM'S BOX THE WHATSAPP ZERO IS ALWAYS A LIE — measured 2026-09-06. Every chat in both stores is archived and handled is maintained on 3 of 1808 rows, so the scan prints whatsapp: every chat is archived and handled is not maintained ... inbox zero, not flag corruption, disarms its own recency net, and reports needs_reply: 0 for personal_nl AND personal_us. That run reported inbox zero while six people were genuinely waiting on a reply. Whenever a note contains "recency net disarmed" or "taken at face value", the zero carries no information: derive needs_reply yourself — a thread needs a reply when its NEWEST message has is_from_me = 0, regardless of the archive flag. Do this with the WhatsApp plugin tools (whatsapp_find, whatsapp_thread), never whatsapp_unread, and never a hand-written sqlite read.

Empty buckets are only trustworthy when the scan says it succeeded. Check the flags before believing a zero:

field meaning what you do
email.empty: true search ran, zero hits real inbox zero
email.reachable: false the call failed fix it, never report zero
whatsapp.blocked: true no store, local or pulled fix ssh/policy, never report zero

On a client box (bridge on another host) the scan pulls the remote store itself over the policy ssh + remote_store entries, using VACUUM INTO for a consistent snapshot. It only reports blocked when that genuinely fails.

ARCHIVED MEANS "DEALT WITH AS OF NOW", NOT FOREVER. WhatsApp never moves the archive flag when a new message lands, so a swept thread that gets a reply an hour later would sit invisible while the scan reports inbox zero (observed 2026-09-06: five people replied within four hours of a sweep). --apply writes a per-chat watermark (ops-sweep-watermarks.json, beside messages.db) and the scan reopens any archived chat whose newest inbound is later than that mark, noting how many. An archived chat that stayed quiet stays archived, and history stays shut: the reopen is bounded by --days.

Read state is not the test, on WhatsApp either. Use whatsapp_find / whatsapp_thread and classify by direction (newest message is_from_me = 0 == needs_reply), never whatsapp_unread — a thread Sam opened on his phone and never answered is empty in any unread listing but still owes a reply.

CROSS-ACCOUNT: a reply on one number counts for the other. One person often exists in both stores under different jids. A reply sent from account A lands only in A's database, so B's copy still ends on their inbound line and looks unanswered. The scan therefore reads every OTHER account's store as read-only evidence and demotes such a thread to waiting with reconciled: "answered from another WhatsApp account". That discovery is automatic; --peer-store DB names one explicitly and --no-peer-stores turns it off. Never draft a reply for a thread carrying a reconciled field.

ARCHIVE/KEEP SPLIT — bin/ops-inbox-archive-set. The scan says what the inbox looks like; this turns that into the two lists inbox-zero actually needs, deterministically, instead of re-deciding a few hundred rows by eye every run:

"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-archive-set"                      # report only
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-scan" | \
  "$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-archive-set" -                  # reuse a scan
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-archive-set" --scan /tmp/scan.json --json
# extra WhatsApp account (repeat the pair per account):
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-archive-set" \
  --wa-store ~/.local/share/whatsapp-mcp/whatsapp-bridge-<label>/store/messages.db \
  --bridge-port <port>
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-archive-set" --apply               # AFTER approval
  • ARCHIVE — WAITING (you sent last), courtesy tails with no open ask ("thanks!", "will do!!"), threads past --stale-days (7), dead groups past --dead-group-days (30).
  • REVIEW (FYI) — newsletters, broadcasts, automated mail. Never swept. Read it, brief the user, then ask; --archive-fyi is the opt-in that answer unlocks. See the FYI core principle below.
  • Undescribed media ([image], [voice] with no enrichment yet) is unknown, never a tail — it always lands in KEEP.
  • KEEP — every genuine unanswered ask, plus every email carrying a todo/action label. Ambiguity always resolves to KEEP; keeping is safe, archiving is the risky direction.
  • Report-only by default. It archives nothing without --apply, and never sends. Present the counts plus the KEEP list with AskUserQuestion, and only re-run with --apply after an explicit OK. --apply --dry-run walks the path without calling out.
  • It enforces the todo/action-label HARD GUARDRAIL in code, so a labelled mail cannot be swept even when it looks exactly like a newsletter.

ONE-SHOT TRIAGE: bin/ops-inbox-zero attempts the inbox-zero pipeline (scan → Paperclip SSOT → Slack direct API → dual-JID deep-read → proposed WA/email archive actions → KEEP report) in one shell call. Gmail, Slack, or Paperclip can be skipped when authentication or local services are unavailable; the report surfaces each status. The agent still does the manual nuance refinement (unsure rows + Rule-6 inline drafts + Telegram AskUserQuestion), but the script handles the deterministic 80% in ~5s so the agent only spends tokens on judgment calls.

"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-zero"                   # safe report-only default
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-zero" --archive         # only after explicit approval
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-zero" --no-slack        # skip Slack triage
"$CLAUDE_PLUGIN_ROOT/bin/ops-inbox-zero" --days 14         # wider window

Always run the report-only command first. Present the exact proposed WhatsApp and Gmail archive items/counts with AskUserQuestion; only after explicit approval rerun the same arguments with --archive. Never infer approval from a previous run. unsure items are report-only and are never archived or unarchived by the script.

Outputs:

  • /tmp/ops-inbox-scan-clean.json — raw scan output
  • /tmp/ops-inbox-keep.json — classified KEEP/ARCHIVE/UNSURE rows (incl. Slack DMs)
  • /tmp/slack-triage.txt — Slack direct-API triage (AUTH + human DMs)
  • ~/.local/share/ops-inbox/keep-<ts>.md — final report (agent reads this for Telegram)

Why this exists: the multi-channel scan used to fan out one Workflow subagent per channel. A single real run burned ~330k subagent tokens / 5 agents / ~130s to do work that, for WhatsApp, is a sqlite read, and for Email, is a CLI call. The script does the same classification (and better — it merges each person's lid↔phone chats into one conversation and resolves real names from contacts) for free. Reserve agent fan-out for genuine reasoning, not for reading a database.

ops-inbox-scan JSON always includes whatsapp / email buckets (needs_reply, waiting, groups, fyi) plus whatsapp_account and counts. Partial failure still emits valid JSON. Email rows carrying forgotten: true were archived or filtered out of the inbox but still owe a reply (see forgotten_count and the matching note) — treat them exactly like inbox rows.

What the script does NOT do — and what you do next, in the MAIN session (no subagents):

  1. Slack — never conversations_unreads (unread is not a filter, Sam 2026-09-17; a guard hard-blocks the call). Instead: channels_me {channel_types:"im,mpim"} once to list human DMs, then conversations_history {channel_id, limit:"5d"} per DM to check who spoke last. A DM Sam read on his phone and never answered must surface — an unread listing would miss it entirely.
  2. Telegram — one mcp__plugin_ops_telegram__list_dialogs call (skip the Pocket ops bot dialog — that's automation). Skip if unconfigured.
  3. FULL-THREAD AWARENESS GATE on the few NEEDS_REPLY candidates — the script's WhatsApp buckets are merged-thread, last-direction-correct first passes; its groups entries are explicitly un-classified. Its email needs_reply is an envelope first pass. Before you draft ANY reply, clear the gate per "Processing each channel": for the handful of candidates, read the full thread both directions (incl. [voice]), write the 2-sentence arc, reconcile the user's own phone-sent messages, and demote anything already answered. You are now doing deep reads on ~3 threads, not scanning hundreds — that is the whole point of the split: cheap script-side triage, expensive reasoning only where it pays.

Fan-out for real per-thread volume. After the cheap scan, deep-read and draft in parallel (Workflow default; Agent Teams / Hermes delegate_task fallback). Skip only the trivial case (~1–3 candidates). Mechanics, hard constraints, and the canonical Workflow JS: references/fan-out.md. Fan-out never sends, archives, or mutates — Rule 6 stays in the main session.

Agent Teams support

When CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 is set and Workflow is missing, fan out one read-only scanner per available channel:

TeamCreate("inbox-channels")
Agent(team_name="inbox-channels", name="whatsapp-scanner", ...)

If the flag is NOT set, use Workflow or sequential main-session reads. Full script and hard constraints: references/fan-out.md.

Fallback — Hermes / Grok (delegate_task)

When this skill runs on Hermes or Grok (no Workflow tool, no AskUserQuestion):

  • Fan-out: delegate_task for one read-only scanner per available channel. If that tool is missing, scan sequentially in the main session. Same read-only contract.
  • Approval: numbered options in chat. On Telegram, two turns — full draft as its own bubble, then the [Send] [Edit] [Skip] card. Never bundle drafts. Never put the only copy of the draft in a clipped preview.
  • Plugin-wide table: Rule 10 in CLAUDE.md and hermes-plugin/RUNTIME.md.

Additional resources

Read the matching file before acting. Do not skip.

  • references/cli.md — WhatsApp MCP + gog CLI
  • references/details.md — inbox-zero rules, gates, per-channel processing, ALL CONTEXT SOURCES
  • references/runtime.md — freshness, Mac fallback, version-heal, watcher, all-context sweep
  • references/fan-out.md — Workflow JS, Agent Teams, hard constraints
  • CHANNELS.md — channel setup
  • vip skill — tier order for step 0; who is answered first
  • relations skill — person brief, open commitments, draft context

Version History

  • 057e72d Current 2026-09-22 01:17

    修复了在父会话中自动启动草稿起草的问题,移除了导致父会话空闲的fork机制;默认启用30秒心跳以维持会话连接。

  • 7ea9622 2026-09-08 19:37

    修复因归档标志不随新消息更新导致的漏报问题;引入每聊天水印机制,确保仅重新打开有新回复的已归档对话,提高扫描准确性。

  • 9d2d383 2026-09-02 21:48

    修正多账号场景下的服务器名称解析规则,明确区分普通账号与 CoS 别名;修复日程确认前需查询所有日历和邮箱的逻辑缺陷;新增关系管理与 VIP 技能配置支持。

  • 6f1e3e0 2026-08-27 09:31

    优化专家发现上下文加载性能,修复文件系统竞态条件及索引缺失时的错误处理,恢复市场发布标识并修正路由配置。

  • 5751870 2026-08-19 11:48

    优化运行流程:首次遍历即收集上下文,按紧急度生成草稿卡片;新增[代我处理]选项以委派子代理工作;修复WhatsApp多账号路由问题。

  • 206d40d 2026-08-16 15:49

    整合出站发送网关为共享守卫,解决多账户匹配及脚本绕过问题,增加计数审批支持。

  • 64bad13 2026-08-12 09:00

Same Skill Collection

claude-ops/skills/boss/SKILL.md
claude-ops/skills/flow/SKILL.md
claude-ops/skills/humanizer/SKILL.md
claude-ops/skills/ledger/SKILL.md
claude-ops/skills/ops-accounts/SKILL.md
claude-ops/skills/ops-ar/SKILL.md
claude-ops/skills/ops-aws-audit/SKILL.md
claude-ops/skills/ops-comms/SKILL.md
claude-ops/skills/ops-competitors/SKILL.md
claude-ops/skills/ops-credentials/SKILL.md
claude-ops/skills/ops-daemon/SKILL.md
claude-ops/skills/ops-dash/SKILL.md
claude-ops/skills/ops-deploy-fix/SKILL.md
claude-ops/skills/ops-deploy/SKILL.md
claude-ops/skills/ops-desk/SKILL.md
claude-ops/skills/ops-desktop/SKILL.md
claude-ops/skills/ops-doctor/SKILL.md
claude-ops/skills/ops-ecom/SKILL.md
claude-ops/skills/ops-feature-dev/SKILL.md
claude-ops/skills/ops-fires/SKILL.md
claude-ops/skills/ops-fleet/SKILL.md
claude-ops/skills/ops-go/SKILL.md
claude-ops/skills/ops-gtm/SKILL.md
claude-ops/skills/ops-home/SKILL.md
claude-ops/skills/ops-humanizer/SKILL.md
claude-ops/skills/ops-integrate/SKILL.md
claude-ops/skills/ops-leadgen/SKILL.md
claude-ops/skills/ops-linear/SKILL.md
claude-ops/skills/ops-mac/SKILL.md
claude-ops/skills/ops-marketing/SKILL.md
claude-ops/skills/ops-mcp/SKILL.md
claude-ops/skills/ops-merge/SKILL.md
claude-ops/skills/ops-monitor/SKILL.md
claude-ops/skills/ops-next/SKILL.md
claude-ops/skills/ops-orchestrate/SKILL.md
claude-ops/skills/ops-package/SKILL.md
claude-ops/skills/ops-pocket/SKILL.md
claude-ops/skills/ops-projects/SKILL.md
claude-ops/skills/ops-recap/SKILL.md
claude-ops/skills/ops-release/SKILL.md
claude-ops/skills/ops-resume/SKILL.md
claude-ops/skills/ops-revenue/SKILL.md
claude-ops/skills/ops-rotate-setup/SKILL.md
claude-ops/skills/ops-rotate/SKILL.md
claude-ops/skills/ops-rules/SKILL.md
claude-ops/skills/ops-secret-sync/SKILL.md
claude-ops/skills/ops-settings/SKILL.md
claude-ops/skills/ops-ship/SKILL.md
claude-ops/skills/ops-social-planner/SKILL.md

Metadata

Files
0
Version
057e72d
Hash
7dff9c5b
Indexed
2026-08-12 09:00

trang chủ - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-24 02:54
浙ICP备14020137号-1