Back to the catalog

wu-whatsapp

WhatsApp integration for Claude Code — send messages, search chats, manage groups via wu-cli

Open source Open in the app JSON README (API)

About

WhatsApp integration for Claude Code — send messages, search chats, manage groups via wu-cli

Details

Kind
Plugins
Topic
Communication
Publisher
ibrahimhajjaj
Origin
marketplace
Category
ferramentas
Stars
14
Forks
3
Last push
2026-08-12T21:08:08Z
Repository state
ativo
Language
TypeScript
License
MIT
Added
2026-08-30 01:48:58
Updated
2026-08-30 01:48:58
Origin id
ibrahimhajjaj/wu-cli/wu-whatsapp

README

# wu

WhatsApp CLI tool — like `gh` is to GitHub, `wu` is to WhatsApp.

Built on [@whiskeysockets/baileys](https://github.com/WhiskeySockets/Baileys).

## Install

```bash
npm install -g @ibrahimwithi/wu-cli
```

Requires Node.js 20+.

## Quick Start

```bash
# Authenticate (scan QR code)
wu login

# Or use pairing code instead
wu login --code 15551234567

# Start collecting messages
wu listen

# Search messages
wu messages search "meeting tomorrow"

# Send a message
wu messages send 1234567890@s.whatsapp.net "Hello!"

# List your groups
wu groups list --live
```

By default, wu operates in **opt-in mode** — no messages are collected until you allow specific chats:

```bash
# Allow all group messages (read-only)
wu config allow '*@g.us' --mode read

# Allow full access to a specific chat
wu config allow 1234567890@s.whatsapp.net

# Start collecting
wu listen
```

## Commands

### Authentication

| Command | Description |
|---|---|
| `wu login` | Authenticate with WhatsApp via QR code |
| `wu login --code <phone>` | Authenticate via pairing code |
| `wu logout` | Clear session |
| `wu status` | Show connection status and account info |

### Messages

| Command | Description |
|---|---|
| `wu messages list <jid>` | List messages in a chat |
| `wu messages search <query>` | Full-text search messages (FTS5 ranked) |
| `wu messages send <jid> [text]` | Send text, media, or poll |
| `wu messages react <jid> <id> <emoji>` | React to a message |
| `wu messages delete <jid> <id>` | Delete a message for everyone |
| `wu messages export <jid>` | Export messages to a file (jsonl/json/markdown/csv) |

```bash
# Send with media
wu messages send 1234567890@s.whatsapp.net --media photo.jpg --caption "Check this out"

# Create a poll
wu messages send 120363XXX@g.us --poll "Lunch?" --options "Pizza,Sushi,Tacos"

# Reply to a message
wu messages send 120363XXX@g.us "Agreed" --reply-to BAE5ABC123

# Search within a specific chat
wu messages search "budget" --chat 120363XXX@g.us --limit 20

# Download all undownloaded media in a chat
wu media download-batch 120363XXX@g.us --limit 50 --concurrency 4

# Export all messages since a timestamp to a file
wu messages export 120363XXX@g.us --after 1772548621 --format jsonl --output data/export.jsonl

# Export as readable markdown
wu messages export 120363XXX@g.us --format markdown --output data/chat.md
```

### Chats & Contacts

| Command | Description |
|---|---|
| `wu chats list` | List all chats |
| `wu chats search <query>` | Search chats by name |
| `wu contacts list` | List all contacts |
| `wu contacts search <query>` | Search contacts by name or phone |
| `wu contacts info <jid>` | Show contact details |

### Groups

| Command | Description |
|---|---|
| `wu groups list` | List groups with community tree and constraint status |
| `wu groups list --live` | Fetch from WhatsApp and refresh community linkage |
| `wu groups list --allowed-only` | Skip groups whose constraint is `none` |
| `wu groups info <jid>` | Show group details and participants |
| `wu groups create <name> [jids...]` | Create a new group |
| `wu groups invite <jid>` | Get invite link |
| `wu groups leave <jid>` | Leave a group |
| `wu groups rename <jid> <name>` | Rename a group |
| `wu groups join <code-or-url>` | Join a group by invite code or URL |
| `wu groups participants <jid>` | List group participants |

By default `wu groups list` shows every group you're in (jid + name + community shape) so you can see what's there before opting in. Description, full participant list, and messages stay constraint-gated. Set `whatsapp.group_discovery: false` in the config to revert to the old behavior where group metadata is only stored when the constraint allows.

### Communities

| Command | Description |
|---|---|
| `wu communities list` | List WhatsApp Communities (parent groups) |
| `wu communities list --with-subgroups` | Include linked subgroups under each parent |

### Direct messages (DMs)

| Command | Description |
|---|---|
| `wu dms list` | List 1:1 chats you've opted into |
| `wu dms list --all` | Include DMs blocked by constraints |
| `wu dms search <query>` | Search 1:1 chats by name |

DM JIDs contain the contact's phone number, so they're always constraint-gated regardless of `group_discovery`.

### Media

| Command | Description |
|---|---|
| `wu media download <msg-id>` | Download media from a message |
| `wu media download-batch [jid]` | Download undownloaded media in parallel (`--ids a,b,c` for specific messages) |
| `wu media send <jid> <path>` | Send a media file |
| `wu media transcribe <msg-id>` | Transcribe a voice/audio message to text |
| `wu media ocr <msg-id>` | Extract text from an image message |
| `wu media prune` | Delete downloaded media files (`--older-than 30d`, `--chat`, `--dry-run`) |

Transcripts and OCR text are stored on the message and indexed for `wu messages search`.

### Enrichment

Transcription and OCR run through pluggable backends — a local binary (default) or a hosted API. wu bundles no models; install the binary or set an API key.

```bash
# See which backends are configured and ready, with how to enable them
wu enrich status
```

| Capability | Local default | API option |
|---|---|---|
| `transcribe` | `whisper` (or whisper.cpp via a custom command) | any OpenAI-compatible audio API (Groq, OpenAI) |
| `ocr` | `tesseract` (with `ara+eng` data) | Anthropic vision, or any OpenAI-compatible vision API |

Configure under `enrich` in `config.yaml` (see Configuration below).

### History

| Command | Description |
|---|---|
| `wu history backfill <jid>` | Request older messages from WhatsApp |

```bash
# Backfill 50 older messages for a group
wu history backfill 120363XXX@g.us --count 50

# With JSON output
wu history backfill 120363XXX@g.us --count 20 --json
```

### Daemon

```bash
# Run as a foreground daemon — collects messages continuously
wu daemon

# Install as a systemd service (Linux)
wu daemon install

# Remove systemd service
wu daemon uninstall

# View daemon logs
wu daemon logs
```

The daemon auto-reconnects on connection drops, logs health every 5 minutes, and stores all messages to SQLite.

### Remote Sync

Run the daemon on a VPS collecting messages 24/7, query from your local machine. All communication over SSH — no exposed ports, no extra auth.

```bash
# --- On the VPS ---
npm i -g @ibrahimwithi/wu-cli
wu login
wu daemon install

# --- On your local machine ---
# Add the remote
wu remote add vps user@your-vps-ip

# Push your constraints to the server
wu remote setup vps --push

# Pull the database
wu sync pull

# Continuous sync every 30s
wu sync pull --watch --interval 30

# Install as a systemd timer (Linux)
wu sync install --interval 60
```

| Command | Description |
|---|---|
| `wu remote add <name> <host>` | Add a remote wu instance |
| `wu remote list` | List configured remotes |
| `wu remote remove <name>` | Remove a remote |
| `wu remote default <name>` | Set the default remote |
| `wu remote setup <name>` | Sync constraints between local and remote |
| `wu sync pull [name]` | Pull database from remote |
| `wu sync pull --watch` | Continuously sync on an interval |
| `wu sync install` | Install systemd timer for periodic sync |
| `wu sync uninstall` | Remove systemd sync timer |

When a remote is configured and no local daemon is running, `wu mcp` starts in **remote mode** — reads from the local synced DB, routes writes (send, react, etc.) through SSH to the VPS.

### MCP Server

```bash
# Start MCP server (stdio transport for AI agents)
wu mcp
```

Exposes WhatsApp as tools and resources for AI agents via the [Model Context Protocol](https://modelcontextprotocol.io). See [MCP setup guide](docs/mcp-setup.md) for Claude Code, Cursor, Codex CLI, and Gemini CLI configuration.

The MCP server operates in three modes:

| Mode | Condition | Reads | Writes |
|---|---|---|---|
| Full local | No daemon running, no remote | WhatsApp (live) | WhatsApp (live) |
| Read-only | Local daemon running | SQLite | Disabled |
| Remote | Remote configured + synced DB | Local SQLite | SSH to remote |

### Configuration

| Command | Description |
|---|---|
| `wu config show` | Print current config (YAML) |
| `wu config set <path> <value>` | Set a config value (dot-notation) |
| `wu config path` | Print config file path |
| `wu config allow <jid>` | Allow a chat (default: full access) |
| `wu config block <jid>` | Block a chat (drop all messages) |
| `wu config remove <jid>` | Remove a per-chat constraint |
| `wu config default [mode]` | Get/set default constraint mode |
| `wu config constraints` | Show all constraints |

### Database

| Command | Description |
|---|---|
| `wu db vacuum` | Run VACUUM and ANALYZE |
| `wu db reindex` | Rebuild the full-text search index (fixes `database disk image is malformed` on search) |

## Constraints

The constraint system controls what wu can do with each chat. Three modes:

| Mode | Collect messages | Send messages | Manage group |
|---|---|---|---|
| `full` | yes | yes | yes |
| `read` | yes | no | no |
| `none` | no | no | no |

Resolution order (most specific wins):
1. Exact JID match (`1234567890@s.whatsapp.net`)
2. Wildcard domain (`*@g.us` for all groups, `*@s.whatsapp.net` for all DMs)
3. Default constraint
4. Implicit fallback: `none`

```bash
# Set default to read-only for everything
wu config default read

# Full access for one group
wu config allow 120363XXX@g.us

# Block a specific chat
wu config block 1234567890@s.whatsapp.net

# Read-only for all groups
wu config allow '*@g.us' --mode read
```

## Configuration

Config lives at `~/.wu/config.yaml`:

```yaml
whatsapp:
  read_receipts: false     # Send read receipts (default: false)
  media_max_mb: 50         # Max media auto-download size in MB
  send_delay_ms: 1000      # Delay before sending messages (ms)
  group_discovery: true    # Cache group metadata even when constraint is 'none'
                           # (jid + name + community shape only; descriptions
                           # and participants stay constraint-gated). Set to
                           # false for strict mode.

constraints:
  default: none            # Default constraint mode
  chats:
    "*@g.us":
      mode: read
    "120363XXX@g.us":
      mode: full

remotes:
  vps:
    host: user@your-vps-ip
    wu_home: ~/.wu
default_remote: vps

db:
  path: ~/.wu/wu.db        # SQLite database path

log:
  level: info              # debug, info, warn, error

enrich:                    # Media enrichment backends (off until configured)
  transcribe:
    backend: local         # local | api | off
    local:
      cmd: "whisper {input} --model base --output_format txt --output_dir {outdir}"
    api:                   # used when backend: api
      provider: openai     # OpenAI-compatible audio (Groq, OpenAI, ...)
      base_url: https://api.groq.com/openai/v1
      key_env: GROQ_API_KEY
      model: whisper-large-v3
  ocr:
    backend: local
    local:
      cmd: "tesseract {input} stdout -l ara+eng"
    api:
      provider: anthropic  # anthropic (messages vision) | openai (chat vision)
      base_url: https://api.anthropic.com/v1
      key_env: ANTHROPIC_API_KEY
      model: claude-haiku-4-5-20251001
```

The local `cmd` runs with `{input}` replaced by the media path; it must either print the text to stdout (e.g. tesseract) or write a `.txt` into `{outdir}` (e.g. whisper). Run `wu enrich status` to see what's detected and how to enable each backend.

All runtime data lives under `~/.wu/` (override with `WU_HOME` env var).

## JSON Output

Most commands support `--json` for machine-readable output. When piped, `wu listen` auto-detects and switches to JSON:

```bash
# Pipe messages to jq
wu listen | jq '.body'

# Export messages as JSON
wu messages list 120363XXX@g.us --json --limit 1000
```

## MCP Tools and Resources

When running `wu mcp`, the following are available to AI agents:

**Tools:** `wu_messages_send`, `wu_react`, `wu_media_download`, `wu_media_download_batch`, `wu_media_prune`, `wu_media_transcribe`, `wu_media_ocr`, `wu_enrich_status`, `wu_messages_search`, `wu_messages_list`, `wu_messages_context`, `wu_messages_count`, `wu_messages_export`, `wu_history_backfill`, `wu_chats_list`, `wu_chats_search`, `wu_dms_list`, `wu_contacts_list`, `wu_contacts_search`, `wu_groups_list`, `wu_groups_info`, `wu_groups_invite`, `wu_groups_create`, `wu_groups_leave`, `wu_groups_rename`, `wu_groups_join`, `wu_communities_list`, `wu_constraints_list`, `wu_constraints_set`, `wu_constraints_remove`, `wu_constraints_default`, `wu_config_show`, `wu_status`

`wu_messages_export` takes `download_media: true` to download the window's image/document media and write a `<output>.manifest.jsonl` mapping each item to its local file path. Add `enrich: true` to also OCR images and transcribe audio in the same pass, writing the text onto each message and into the manifest rows (`ocr_text`, `transcript`). Enrichment respects the configured backends — a disabled one is skipped and reported, never fatal.

**Resources:** `wu://chats`, `wu://chats/{jid}/messages`, `wu://contacts`, `wu://contacts/{jid}`, `wu://groups`, `wu://groups/{jid}`, `wu://status`

## Exit Codes

| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Constraint violation |
| 3 | Not authenticated |
| 4 | Connection failed |
| 5 | Not found |

## Notes

### Baileys pin policy

`@whiskeysockets/baileys` is pinned to an exact release candidate
(`7.0.0-rc13`) in `package.json` - no `^` or `~`. `@whiskeysockets/baileys` is
a reverse-engineered client for a protocol WhatsApp doesn't publish, so an
`-rc` build can be yanked or shift behavior between builds; an exact pin keeps
deploys deterministic instead of picking up a moving target on `npm install`.

Upgrade cadence: periodically check upstream for a GA `7.0.0` (or newer)
release and bump deliberately - never via `npm update`. Any bump must be
tested against a real WhatsApp session before merging (the characterization
tests in `tests/` can't simulate the live protocol) and followed by `npm
audit` (the `ws` advisory that rides this dependency clears on bump).

## License

MIT

More