comms-whatsapp
Skill22 starsChanged 2 months agoMITView on GitHub
Read and send WhatsApp messages with wacli. Use to search messages, list chats, groups, or contacts, fetch threads or media, or send WhatsApp messages.
- Installs packages
We split this skill into the rules it gives an agent
15 rules. About 0 are common, 3 are distinctive, and 12 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
12
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 36
After pairing, kick off an initial sync that exits when idle:
- Very specific: line 50
Check first: Run wacli doctor / wacli auth status before pairing.
- Very specific: line 52
Linux store: Default store is ~/.local/state/wacli (XDG).
- Very specific: line 54
Install on Linux if missing: Prefer the GitHub release binary (openclaw/wacli, linux_amd64) onto ~/.local/bin/wacli.
- Very specific: line 58
Default output is human-formatted (columns, truncation, senders shown as bare @lid numbers).
- Very specific: line 60
Every --json response is an envelope: {"success":bool, "data":…, "error":…}.
- Distinctive: line 181
Search returned a media message ID?
- Very specific: line 198
If LOCKED true and the user isn't expecting a sync running, kill it:
- Distinctive: line 213
If a hit looks promising, fetch context:
- Very specific: line 250
Don't run wacli send ... while a wacli sync --follow is up — store is locked.
- Very specific: line 251
Don't paste a phone number with + or spaces to --to — strip to digits only (e.g.
- Very specific: line 252
Don't try to send to a JID you fished out of message metadata that ends in @lid (linked device alias) — use the @s.whatsapp.net form.
- Very specific: line 253
Don't attribute a quote from the sender JID, the human output, or DisplayText/Snippet — only FromMe (direction) + the message's own Text …
- Very specific: line 254
Don't expect old history to be there.
- Very specific: line 255
Don't commit ~/.wacli/ — it contains session keys.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
wacli — WhatsApp from the CLI
wacli is a Go CLI built on the whatsmeow library. It pairs to the user's WhatsApp account as a multi-device companion (like WhatsApp Web), syncs messages to a local SQLite DB at ~/.wacli/wacli.db, and exposes commands to read, search, and send.
Repo: https://github.com/steipete/wacli · install: brew install steipete/tap/wacli.
Mental model — sync vs. read
Two distinct phases:
- Sync (
wacli sync/wacli auth --follow) connects to WhatsApp servers and writes new messages to the local DB. Runs as a long-lived process. New messages only land in the DB while sync is running. - Read (
wacli messages list/search/show,wacli chats list) queries the local DB. Fast, offline, no network. Always reads what sync has already written.
So when the user asks "what did Alice send yesterday?" — the answer comes from the local DB. If the DB is stale, the user needs to run sync first. Use wacli doctor to check state.
Critical gotchas
- Single-writer lock. The store is locked while
wacli sync(or any connecting command) holds it. Trying to send while sync is running fails with a lock error. Tell the user to stop sync (pkill -f 'wacli sync') before sending, or runwacli send ...in a window where sync isn't running.wacli doctorshows lock state. - History is shallow by default. Initial sync only pulls recent messages from the user's primary device. Older history requires
wacli history backfill --chat <jid> --requests N. Best-effort, may not return. - JIDs, not phone numbers. Most read commands take
--chat <jid>(e.g.61400000000@s.whatsapp.netfor DMs,<id>@g.usfor groups).wacli send textaccepts--toas either phone or JID. To find a JID:wacli chats list --jsonorwacli contacts search "name" --json. - FTS5 may not be available.
wacli doctorshowsFTS5 falseon stock macOS SQLite — search falls back to LIKE, which is slower and less precise. Still usable for typical agent queries. - Do not commit
~/.wacli/. It contains the session keypair and message DB.
Setup (once)
brew install steipete/tap/wacli
wacli auth # shows QR; pair from phone → WhatsApp → Linked DevicesAfter pairing, kick off an initial sync that exits when idle:
wacli sync --once --idle-exit 30sOr leave a follower running in the background to keep the local DB hot:
wacli sync --follow >/tmp/wacli-sync.log 2>&1 &Shared computer / already paired
- Check first. Run
wacli doctor/wacli auth statusbefore pairing. IfAUTHENTICATED true, do not runwacli authagain — that creates another linked device. - Shared machine. The binary and store live on the shared home directory. Any agent on that computer uses the same
waclisession. No second install, no second QR. - Linux store. Default store is
~/.local/state/wacli(XDG). macOS is~/.wacli. Binary is often~/.local/bin/wacli. - One lock.
wacli sync/wacli authholds a single-writer lock for everyone. Do not leavewacli sync --followrunning. Reads (messages search/list,chats list) are fine when unlocked. Stop sync before send. - Install on Linux if missing. Prefer the GitHub release binary (
openclaw/wacli, linux_amd64) onto~/.local/bin/wacli. Homebrew tapopenclaw/tap/waclialso works where brew exists.
Always pass --json when an agent is consuming output
Default output is human-formatted (columns, truncation, senders shown as bare @lid numbers). --json produces structured rows that pipe straight into jq. Use it everywhere the result feeds back to Claude — and never attribute a quote from the human output (see "Attribution" below).
Every --json response is an envelope: {"success":bool, "data":…, "error":…}. The payload is under .data:
messages list/search→.data.messages[], each with PascalCase keys:ChatJID,ChatName,SenderJID,Timestamp,FromMe,Text,DisplayText,MediaType,Snippet.chats list→.data[]withJID,Kind,Name,LastMessageTS.contacts search→.data[]withJID,Phone,Name,Alias,Tags.
wacli messages search "magic tags" --limit 20 --json \
| jq '.data.messages[] | {ChatName, FromMe, Timestamp, Text}'Attribution — who sent each message (read before quoting anyone)
Getting "who said X" wrong is the most common and most damaging wacli mistake. Two traps cause it; both are avoided by the same two rules.
FromMeis the only reliable speaker signal — never infer the sender from the JID or the human output. In a DM both sides come back. Only the account owner is reliably identifiable, via the booleanFromMe. The other party'sSenderJIDis a raw@lid(e.g.191624378347690@lid), not a name, and the same person appears under device variants (…@lid,…:19@lid). The human output prints your messages asmeand theirs as the bare@lid. Rule: attribute every quote fromFromMe—true= the account owner (you),false= the other party. Resolve a@lidto a name withwacli contacts search/showonly for labelling, never for deciding direction.Textis the message's own words;DisplayText/Snippetbundle quoted-reply context — so a search can pin a phrase on the wrong author. When B replies to A, B'sDisplayText/Snippetcontains A's quoted line, so a substring search matches both A's original and B's reply — making it look like B said A's words. Rule: when deciding who said a phrase, match theTextfield only — neverDisplayTextorSnippet.
Correct attribution recipe — a clean transcript labelled by FromMe, using each message's own Text:
wacli messages list --chat <jid> --limit 200 --json \
| jq -r '.data.messages[] | select(.Text != "")
| (if .FromMe then "ME " else "THEM" end) + " | " + .Timestamp + " | " + .Text'Build any "who said what" answer from this — not from the human output, not from DisplayText. If a phrase isn't in anyone's own Text (only in quoted replies), it means nobody in the window said it as their own message — don't attribute it, and history backfill if it predates the sync window.
Read recipes
Find the chat / contact JID
# Browse chats sorted by recent activity
wacli chats list --limit 30 --json
# Substring match on a contact name
wacli contacts search "Alice" --json
# Show full contact record (aliases, tags, JID, push name)
wacli contacts show --jid <jid> --jsonSearch messages
# Global search across all chats
wacli messages search "<query>" --limit 50 --json
# Constrain to a chat
wacli messages search "<query>" --chat <jid> --json
# Constrain by sender (DMs include both sides; --from filters senders)
wacli messages search "<query>" --from <jid> --json
# Time window (RFC3339 or YYYY-MM-DD; --before is exclusive)
wacli messages search "<query>" --after 2026-04-01 --before 2026-04-28 --json
# Only media of a specific type
wacli messages search "" --chat <jid> --type image --jsonList a chat's recent messages
wacli messages list --chat <jid> --limit 100 --json
wacli messages list --chat <jid> --after 2026-04-25 --jsonGet context around a message
Once a search returns a hit, expand the surrounding thread:
wacli messages context --chat <jid> --id <message-id> --before 10 --after 10 --jsonPull older messages a primary device hasn't yet shared
wacli history backfill --chat <jid> --count 50 --requests 3 --wait 60sThis asks the user's phone to send a history slab. Best-effort — phone must be online and recently active.
Send recipes
Stop any running
wacli syncbefore send commands, or they'll fail on the store lock.
Text
wacli send text --to <phone-or-jid> --message "your text"
# Phone form is auto-converted to JID; omit `+`. E.g. --to 61400000000File (image / video / audio / document)
wacli send file --to <jid> --file /abs/path.png --caption "look at this"
wacli send file --to <jid> --file /abs/notes.pdf --filename "Meeting notes.pdf"--mime overrides detection if wacli mis-classifies an attachment.
Group management
wacli groups list --json # known groups (from local DB)
wacli groups refresh # pull fresh group list from server
wacli groups info --jid <group-jid> # participants, name, description
wacli groups join --code <invite-code>
wacli groups leave --jid <group-jid>
wacli groups participants add --jid <group> --participants <jid>,<jid>
wacli groups participants remove --jid <group> --participants <jid>
wacli groups invite get --jid <group-jid> # current invite linkMedia download
Search returned a media message ID? Pull the binary:
wacli media download --chat <jid> --id <message-id> --output /tmp/mediaOutputs to the configured media dir under the store by default.
Diagnostics
wacli doctor # store path, lock state, auth, FTS availability
wacli doctor --connect # try a live connection (requires lock be free)
wacli auth status # auth-only check
wacli versionIf LOCKED true and the user isn't expecting a sync running, kill it:
pkill -f 'wacli sync' && wacli doctorCommon agent flows
"Find that WhatsApp thread where we talked about X"
wacli messages search "X" --limit 30 --json \
| jq '.data.messages[] | {ChatName, FromMe, Timestamp, Text}'If a hit looks promising, fetch context:
wacli messages context --chat <jid> --id <message-id> --before 8 --after 8 --json"Send Alice the link to Y"
ALICE=$(wacli contacts search "Alice" --json | jq -r '.data[0].JID')
wacli send text --to "$ALICE" --message "Y: https://...""What's in the family group lately?"
GROUP=$(wacli groups list --json | jq -r '.data[] | select(.Name=="Family") | .JID')
wacli messages list --chat "$GROUP" --limit 50 --after $(date -v-7d +%Y-%m-%d) --jsonConfirm sync is fresh before reporting
wacli sync --once --idle-exit 15s # foreground catch-up, ~15s
wacli messages list --chat <jid> --limit 5 --jsonOutput flags reference
Every command supports:
--json— structured output (use this when piping to jq or returning to Claude)--store <dir>— alternate store dir (default~/.waclior$WACLI_STORE_DIR)--timeout <duration>— bound non-sync commands (default 5m)
What NOT to do
- Don't run
wacli send ...while awacli sync --followis up — store is locked. Stop sync first. - Don't paste a phone number with
+or spaces to--to— strip to digits only (e.g.61400000000). - Don't try to send to a JID you fished out of message metadata that ends in
@lid(linked device alias) — use the@s.whatsapp.netform.wacli contacts showreturns the right one. - Don't attribute a quote from the sender JID, the human output, or
DisplayText/Snippet— onlyFromMe(direction) + the message's ownText(content) are reliable. See "Attribution" above. - Don't expect old history to be there. Run
wacli history backfillfor a specific chat if the user asks about messages older than the sync window. - Don't commit
~/.wacli/— it contains session keys.
In this repository194 other files it gives its agents.
- 0-templateSkill
- ab-test-analyzerSkill
- academy-guideSkill
- add-to-my-skillsSkill
- adr-authorSkill
- ai-tools-setupSkill
- algorithmic-artSkill
- api-and-interface-designSkill
- api-designerSkill
- api-docs-writerSkill
- apple-store-submitSkill
- aws-cost-optimizerSkill
- bash-script-hardeningSkill
- blog-post-writerSkill
- book-summarizerSkill
- branch-cleanupSkill
- brand-copy-landingSkill
- brand-copySkill
- brand-designSkill
- brand-guidelinesSkill
- browser-testing-with-devtoolsSkill
- canvas-designSkill
- changelog-generatorSkill
- changelog-keeperSkill
- chart-chooserSkill
- ci-cd-and-automationSkill
- ci-fix-loopSkill
- claude-apiSkill
- cloudflare-block-countriesSkill
- coderabbitloopSkill
- code-review-and-qualitySkill
- code-reviewerSkill
- code-simplificationSkill
- codexloopSkill
- comms-notionSkill
- comms-slackSkill
- competitive-analysisSkill
- concept-explainerSkill
- constraint-driven-developmentSkill
- context-engineeringSkill
- context-repoSkill
- cover-letter-writerSkill
- crash-courseSkill
- cron-schedulerSkill
- data-digestSkill
- data-pipeline-architectSkill
- debug-detectiveSkill
- debugging-and-error-recoverySkill
- decision-matrixSkill
- dependabot-triageSkill
- dependency-upgraderSkill
- deprecation-and-migrationSkill
- dev-instantdbSkill
- discernment-nudgeSkill
- doc-coauthoringSkill
- dockerfile-proSkill
- docs-index-keeperSkill
- documentation-and-adrsSkill
- docxSkill
- doubt-driven-developmentSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

