--- name: contacts description: "Contact management with CardDAV sync. Validates email recipients for calendar invites and himalaya email sending. Prevents hallucinated addresses." metadata: {"clawdbot":{"emoji":"📇","requires":{"bins":["python3","vdirsyncer"]}}} --- # Contacts Manage a local vCard contact list synced to Migadu CardDAV via vdirsyncer. Used by the calendar tool and himalaya wrapper to validate recipient addresses before sending. ## Why This Exists LLMs can hallucinate email addresses — inventing plausible-looking addresses from context instead of looking up the correct one. This contact list serves as a **tool-level allowlist**: outbound emails can only go to addresses that exist in the contacts. **Adding contacts and sending emails are separate operations.** Never add a contact and send to it in the same request. ## Prerequisites - Python 3 (no external dependencies) - `vdirsyncer` configured with a CardDAV pair for contacts sync - Contacts directory: `~/.openclaw/workspace/contacts/` (subdirs per address book, e.g. `family/`, `business/`) ## Usage ```bash SKILL_DIR=~/.openclaw/workspace/skills/contacts # List all contacts $SKILL_DIR/scripts/contacts.sh list # Add a contact (single email) $SKILL_DIR/scripts/contacts.sh add --name "小鹿" --email "mail@luyx.org" # Add with email type and nickname $SKILL_DIR/scripts/contacts.sh add --name "小橘子" --email "Erica.Jiang@anderson.ucla.edu" --type work --nickname "小橘子" # Add a second email to an existing contact $SKILL_DIR/scripts/contacts.sh add --name "小橘子" --email "xueweijiang0313@gmail.com" --type home # Resolve a name to email address (used by other tools) $SKILL_DIR/scripts/contacts.sh resolve "小橘子:work" # Output: Erica.Jiang@anderson.ucla.edu # Delete a contact $SKILL_DIR/scripts/contacts.sh delete --name "小橘子" ``` ## Resolve Formats The `resolve` command accepts three formats: | Format | Example | Behavior | |--------|---------|----------| | Name | `小橘子` | Match by FN or NICKNAME. Error if multiple emails (use type). | | Name:type | `小橘子:work` | Match by name, select email by type. | | Raw email | `user@example.com` | Accept only if the email exists in contacts. | Unknown addresses are **rejected** with the available contacts list shown. ## Integration - **Calendar tool** (`cal_tool.py`): `send --to` calls `contacts.py resolve` before sending invites - **Himalaya wrapper** (`himalaya.sh`): validates To/Cc/Bcc headers before passing to himalaya for email delivery ## Data Contacts are stored as vCard 3.0 `.vcf` files in `~/.openclaw/workspace/contacts//`, synced to Migadu CardDAV via vdirsyncer. Collections match Migadu address books (e.g. `family/`, `business/`). New contacts are added to `family/` by default. The resolve command searches all collections.