Refactors the contacts system from being embedded in cal_tool.py into a standalone contacts skill that serves as the single source of truth for recipient validation across all outbound email paths. - New skills/contacts/ skill: list, add, delete, resolve commands - New skills/himalaya/scripts/himalaya.sh wrapper: validates To/Cc/Bcc recipients against contacts for message send, template send, and message write commands; passes everything else through unchanged - cal_tool.py now delegates to contacts.py resolve instead of inline logic - TOOLS.md updated: agent should use himalaya wrapper, not raw himalaya
628 lines
18 KiB
Markdown
628 lines
18 KiB
Markdown
# Testing the Calendar Skill
|
|
|
|
End-to-end tests for contacts, send, reply, todo, calendar sync, and local calendar. All commands use `--dry-run` first, then live.
|
|
|
|
**Important**: Tests 1-3 (contacts) must run first — `send` requires recipients to be in the contacts list.
|
|
|
|
```bash
|
|
SKILL_DIR=~/.openclaw/workspace/skills/calendar
|
|
CONTACTS_DIR=~/.openclaw/workspace/skills/contacts
|
|
|
|
# Use a date 3 days from now for test events
|
|
TEST_DATE=$(date -d "+3 days" +%Y-%m-%d)
|
|
```
|
|
|
|
---
|
|
|
|
## 1. Contact Add and List
|
|
|
|
Set up test contacts needed for send tests.
|
|
|
|
```bash
|
|
# Add a contact with a single email
|
|
$CONTACTS_DIR/scripts/contacts.sh add --name "测试用户" --email "mail@luyx.org"
|
|
|
|
# List contacts
|
|
$CONTACTS_DIR/scripts/contacts.sh list
|
|
|
|
# Add a contact with typed email and nickname
|
|
$CONTACTS_DIR/scripts/contacts.sh add --name "测试多邮箱" --email "work@example.com" --type work --nickname "多邮箱"
|
|
|
|
# Add a second email to the same contact
|
|
$CONTACTS_DIR/scripts/contacts.sh add --name "测试多邮箱" --email "home@example.com" --type home
|
|
|
|
# List again — should show both emails
|
|
$CONTACTS_DIR/scripts/contacts.sh list
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] `contact add` prints "Added contact: ..."
|
|
- [ ] Second `contact add` prints "Updated contact: ... — added ..."
|
|
- [ ] `contact list` shows all contacts with email types
|
|
- [ ] `.vcf` files created in `~/.openclaw/workspace/contacts/default/`
|
|
|
|
## 2. Recipient Resolution (Send Validation)
|
|
|
|
Test that `send --to` correctly resolves contacts and rejects unknown addresses.
|
|
|
|
```bash
|
|
# Name resolves (single email contact) — should work
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试用户" \
|
|
--subject "Resolve Test" --summary "Resolve Test" \
|
|
--start "${TEST_DATE}T15:00:00" --end "${TEST_DATE}T16:00:00" \
|
|
--dry-run
|
|
|
|
# Name:type resolves — should work
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试多邮箱:work" \
|
|
--subject "Resolve Test" --summary "Resolve Test" \
|
|
--start "${TEST_DATE}T15:00:00" --end "${TEST_DATE}T16:00:00" \
|
|
--dry-run
|
|
|
|
# Nickname resolves — should work
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "多邮箱:home" \
|
|
--subject "Resolve Test" --summary "Resolve Test" \
|
|
--start "${TEST_DATE}T15:00:00" --end "${TEST_DATE}T16:00:00" \
|
|
--dry-run
|
|
|
|
# Known raw email resolves — should work
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "mail@luyx.org" \
|
|
--subject "Resolve Test" --summary "Resolve Test" \
|
|
--start "${TEST_DATE}T15:00:00" --end "${TEST_DATE}T16:00:00" \
|
|
--dry-run
|
|
|
|
# Unknown email REJECTED — should FAIL
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "xiaojuzi@meta.com" \
|
|
--subject "Resolve Test" --summary "Resolve Test" \
|
|
--start "${TEST_DATE}T15:00:00" --end "${TEST_DATE}T16:00:00" \
|
|
--dry-run
|
|
|
|
# Multi-email without type REJECTED — should FAIL (ambiguous)
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试多邮箱" \
|
|
--subject "Resolve Test" --summary "Resolve Test" \
|
|
--start "${TEST_DATE}T15:00:00" --end "${TEST_DATE}T16:00:00" \
|
|
--dry-run
|
|
|
|
# Unknown name REJECTED — should FAIL
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "不存在的人" \
|
|
--subject "Resolve Test" --summary "Resolve Test" \
|
|
--start "${TEST_DATE}T15:00:00" --end "${TEST_DATE}T16:00:00" \
|
|
--dry-run
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] First 4 commands succeed (show ICS output)
|
|
- [ ] Unknown email fails with "not found in contacts" + available contacts list
|
|
- [ ] Multi-email without type fails with "has multiple emails. Specify type"
|
|
- [ ] Unknown name fails with "not found" + available contacts list
|
|
|
|
## 3. Contact Delete
|
|
|
|
```bash
|
|
# Delete the multi-email test contact
|
|
$CONTACTS_DIR/scripts/contacts.sh delete --name "测试多邮箱"
|
|
|
|
# Verify it's gone
|
|
$CONTACTS_DIR/scripts/contacts.sh list
|
|
|
|
# Delete by nickname — should fail (contact already deleted)
|
|
$CONTACTS_DIR/scripts/contacts.sh delete --name "多邮箱"
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Delete prints "Deleted contact: 测试多邮箱"
|
|
- [ ] `contact list` no longer shows that contact
|
|
- [ ] Second delete fails with "No contact matching"
|
|
- [ ] `.vcf` file removed from contacts dir
|
|
|
|
---
|
|
|
|
## 4. Dry Run: Send Invite
|
|
|
|
**Prerequisite**: "测试用户" contact from test 1 must exist.
|
|
|
|
Generates the ICS and MIME email without sending. Check that:
|
|
- ICS has `METHOD:REQUEST`
|
|
- MIME has `Content-Type: text/calendar; method=REQUEST`
|
|
- Only `--to` recipients appear as attendees
|
|
- Times and timezone look correct
|
|
- ICS has `BEGIN:VALARM` with correct `TRIGGER` duration
|
|
|
|
```bash
|
|
# Default alarm (1 day before)
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试用户" \
|
|
--subject "Test Invite" \
|
|
--summary "Test Event" \
|
|
--start "${TEST_DATE}T15:00:00" \
|
|
--end "${TEST_DATE}T16:00:00" \
|
|
--dry-run
|
|
|
|
# Custom alarm (1 hour before)
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试用户" \
|
|
--subject "Test Invite (1h alarm)" \
|
|
--summary "Test Event (1h alarm)" \
|
|
--start "${TEST_DATE}T15:00:00" \
|
|
--end "${TEST_DATE}T16:00:00" \
|
|
--alarm 1h \
|
|
--dry-run
|
|
```
|
|
|
|
## 5. Live Send: Self-Invite
|
|
|
|
Send a real invite to `mail@luyx.org` only (no confirmation needed per email rules).
|
|
|
|
```bash
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试用户" \
|
|
--subject "Calendar Skill Test" \
|
|
--summary "Calendar Skill Test" \
|
|
--start "${TEST_DATE}T15:00:00" \
|
|
--end "${TEST_DATE}T16:00:00" \
|
|
--location "Test Location"
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Script exits without error
|
|
- [ ] Email arrives at `mail@luyx.org`
|
|
- [ ] Email shows Accept/Decline/Tentative buttons (not just an attachment)
|
|
- [ ] `.ics` file saved to `~/.openclaw/workspace/calendars/home/`
|
|
|
|
## 6. Verify Calendar Sync and Local Calendar
|
|
|
|
After sending in step 2, check that the event synced and appears locally.
|
|
|
|
```bash
|
|
# Check vdirsyncer sync ran (should have printed "Synced to CalDAV server" in step 2)
|
|
# If not, run manually:
|
|
vdirsyncer sync
|
|
|
|
# List .ics files in local calendar
|
|
ls ~/.openclaw/workspace/calendars/home/
|
|
|
|
# Check the event shows up in khal
|
|
khal list "$TEST_DATE"
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] `vdirsyncer sync` completes without errors
|
|
- [ ] `.ics` file exists in `~/.openclaw/workspace/calendars/home/`
|
|
- [ ] `khal list` shows "Calendar Skill Test" on the test date
|
|
|
|
## 7. Reply: Accept the Self-Invite
|
|
|
|
The invite sent in step 2 should be in the inbox. Find it, then accept it. This tests the full reply flow without needing an external sender.
|
|
|
|
```bash
|
|
# Find the test invite in inbox
|
|
himalaya envelope list
|
|
|
|
# Confirm it's the calendar invite
|
|
himalaya message read <envelope-id>
|
|
|
|
# Accept it
|
|
$SKILL_DIR/scripts/calendar.sh reply \
|
|
--envelope-id <envelope-id> \
|
|
--action accept
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Reply sent to organizer (youlu@luyanxin.com, i.e. ourselves)
|
|
- [ ] Event saved to `~/.openclaw/workspace/calendars/home/`
|
|
- [ ] `vdirsyncer sync` ran
|
|
- [ ] `khal list "$TEST_DATE"` still shows the event
|
|
|
|
## 8. Reply: Decline an Invite
|
|
|
|
Send another self-invite, then decline it. This verifies decline removes the event from local calendar.
|
|
|
|
```bash
|
|
# Send a second test invite
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试用户" \
|
|
--subject "Decline Test" \
|
|
--summary "Decline Test Event" \
|
|
--start "${TEST_DATE}T17:00:00" \
|
|
--end "${TEST_DATE}T18:00:00"
|
|
|
|
# Find it in inbox
|
|
himalaya envelope list
|
|
|
|
# Decline it
|
|
$SKILL_DIR/scripts/calendar.sh reply \
|
|
--envelope-id <envelope-id> \
|
|
--action decline \
|
|
--comment "Testing decline flow."
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Reply sent to organizer with comment
|
|
- [ ] Event removed from local calendar
|
|
- [ ] `khal list "$TEST_DATE"` does NOT show "Decline Test Event"
|
|
|
|
## 9. Verify Final Calendar State
|
|
|
|
After all tests, confirm the calendar is in a clean state.
|
|
|
|
```bash
|
|
# Sync one more time
|
|
vdirsyncer sync
|
|
|
|
# Only the accepted event should remain
|
|
khal list "$TEST_DATE"
|
|
|
|
# List all upcoming events
|
|
khal list today 7d
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Dry Run: Add Todo
|
|
|
|
Generates the VTODO ICS without saving. Check that:
|
|
- ICS has `BEGIN:VTODO`
|
|
- ICS has correct `PRIORITY` value (1 for high)
|
|
- ICS has `STATUS:NEEDS-ACTION`
|
|
- ICS has `BEGIN:VALARM`
|
|
|
|
```bash
|
|
$SKILL_DIR/scripts/calendar.sh todo add \
|
|
--summary "Test Todo" \
|
|
--due "$TEST_DATE" \
|
|
--priority high \
|
|
--dry-run
|
|
```
|
|
|
|
## 11. Live Add: Create a Todo
|
|
|
|
```bash
|
|
$SKILL_DIR/scripts/calendar.sh todo add \
|
|
--summary "Test Todo" \
|
|
--due "$TEST_DATE" \
|
|
--priority medium \
|
|
--description "Test description"
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Script exits without error
|
|
- [ ] `.ics` file created in `~/.openclaw/workspace/calendars/tasks/`
|
|
- [ ] `todo list` (todoman directly) shows "Test Todo"
|
|
- [ ] `vdirsyncer sync` ran
|
|
|
|
## 12. List Todos
|
|
|
|
```bash
|
|
# Via our wrapper (formatted Chinese output)
|
|
$SKILL_DIR/scripts/calendar.sh todo list
|
|
|
|
# Via todoman directly (should show the same items)
|
|
todo list
|
|
|
|
# Include completed
|
|
$SKILL_DIR/scripts/calendar.sh todo list --all
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] "Test Todo" appears in both outputs
|
|
- [ ] Priority grouping is correct in wrapper output
|
|
- [ ] `--all` flag works (same output when none are completed)
|
|
|
|
## 13. Edit a Todo
|
|
|
|
Change the due date and priority of the test todo from step 8.
|
|
|
|
```bash
|
|
# Edit due date
|
|
$SKILL_DIR/scripts/calendar.sh todo edit --match "Test Todo" --due "$(date -d '+5 days' +%Y-%m-%d)"
|
|
|
|
# Verify change
|
|
$SKILL_DIR/scripts/calendar.sh todo list
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Script exits without error
|
|
- [ ] Output shows "Updated todo: Test Todo" with the change
|
|
- [ ] `todo list` shows the new due date
|
|
- [ ] `vdirsyncer sync` ran
|
|
|
|
```bash
|
|
# Edit priority
|
|
$SKILL_DIR/scripts/calendar.sh todo edit --match "Test Todo" --priority high
|
|
|
|
# Verify change
|
|
$SKILL_DIR/scripts/calendar.sh todo list
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Priority changed to high
|
|
- [ ] Todo appears under the high priority group in formatted output
|
|
|
|
```bash
|
|
# Edit multiple fields at once
|
|
$SKILL_DIR/scripts/calendar.sh todo edit --match "Test Todo" --due "$TEST_DATE" --priority low
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Both due date and priority updated in one command
|
|
|
|
```bash
|
|
# Error: no matching todo
|
|
$SKILL_DIR/scripts/calendar.sh todo edit --match "nonexistent" --due "$TEST_DATE"
|
|
# Should print error and exit non-zero
|
|
|
|
# Error: no fields to edit
|
|
$SKILL_DIR/scripts/calendar.sh todo edit --match "Test Todo"
|
|
# Should print "Nothing to change" message
|
|
```
|
|
|
|
## 14. Complete a Todo
|
|
|
|
```bash
|
|
$SKILL_DIR/scripts/calendar.sh todo complete --match "Test Todo"
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] `todo list` (todoman) — "Test Todo" no longer appears
|
|
- [ ] `$SKILL_DIR/scripts/calendar.sh todo list` — also gone
|
|
- [ ] `$SKILL_DIR/scripts/calendar.sh todo list --all` — appears as completed (with checkmark)
|
|
- [ ] `vdirsyncer sync` ran
|
|
|
|
## 15. Delete a Todo
|
|
|
|
Create a second test todo, then delete it.
|
|
|
|
```bash
|
|
# Create
|
|
$SKILL_DIR/scripts/calendar.sh todo add \
|
|
--summary "Delete Me Todo" \
|
|
--due "$TEST_DATE" \
|
|
--priority low
|
|
|
|
# Confirm it appears
|
|
todo list
|
|
|
|
# Delete
|
|
$SKILL_DIR/scripts/calendar.sh todo delete --match "Delete Me"
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] .ics file removed from tasks dir
|
|
- [ ] `todo list` (todoman) does not show "Delete Me Todo"
|
|
- [ ] `vdirsyncer sync` ran
|
|
|
|
## 16. Todo Check (Cron Output)
|
|
|
|
```bash
|
|
# Create a test todo
|
|
$SKILL_DIR/scripts/calendar.sh todo add \
|
|
--summary "Check Test Todo" \
|
|
--due "$TEST_DATE"
|
|
|
|
# Run check
|
|
$SKILL_DIR/scripts/calendar.sh todo check
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Output matches daily digest format (priority groups, urgency labels)
|
|
- [ ] Complete the todo, run `todo check` again — silent exit (no output)
|
|
|
|
```bash
|
|
$SKILL_DIR/scripts/calendar.sh todo complete --match "Check Test"
|
|
$SKILL_DIR/scripts/calendar.sh todo check
|
|
# Should produce no output
|
|
```
|
|
|
|
## 17. Dry Run: Recurring Event (--rrule)
|
|
|
|
Test recurring event generation. Use a date that falls on a Tuesday.
|
|
|
|
```bash
|
|
# Find next Tuesday
|
|
NEXT_TUE=$(python3 -c "from datetime import date,timedelta; d=date.today(); d+=timedelta((1-d.weekday())%7 or 7); print(d)")
|
|
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试用户" \
|
|
--subject "Recurring Test (Tue)" \
|
|
--summary "Recurring Test (Tue)" \
|
|
--start "${NEXT_TUE}T14:30:00" \
|
|
--end "${NEXT_TUE}T15:00:00" \
|
|
--rrule "FREQ=WEEKLY;COUNT=4;BYDAY=TU" \
|
|
--dry-run
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] ICS has `RRULE:FREQ=WEEKLY;BYDAY=TU;COUNT=4`
|
|
- [ ] DTSTART falls on a Tuesday
|
|
- [ ] No validation errors
|
|
|
|
## 18. Validation: DTSTART/BYDAY Mismatch
|
|
|
|
Verify the tool rejects mismatched DTSTART and BYDAY.
|
|
|
|
```bash
|
|
# This should FAIL — start is on a Tuesday but BYDAY=TH
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试用户" \
|
|
--subject "Mismatch Test" \
|
|
--summary "Mismatch Test" \
|
|
--start "${NEXT_TUE}T09:00:00" \
|
|
--end "${NEXT_TUE}T09:30:00" \
|
|
--rrule "FREQ=WEEKLY;COUNT=4;BYDAY=TH" \
|
|
--dry-run
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Script exits with error
|
|
- [ ] Error message says DTSTART falls on TU but RRULE says BYDAY=TH
|
|
- [ ] Suggests changing --start to a date that falls on TH
|
|
|
|
## 19. Event List
|
|
|
|
```bash
|
|
# List upcoming events
|
|
$SKILL_DIR/scripts/calendar.sh event list
|
|
|
|
# Search by text
|
|
$SKILL_DIR/scripts/calendar.sh event list --search "Calendar Skill Test"
|
|
|
|
# List with UIDs
|
|
$SKILL_DIR/scripts/calendar.sh event list --format "{uid} {title}"
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Events from earlier tests appear
|
|
- [ ] Search narrows results correctly
|
|
- [ ] UIDs are displayed with --format
|
|
|
|
## 20. Event Delete
|
|
|
|
```bash
|
|
# Send a throwaway event first
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试用户" \
|
|
--subject "Delete Me Event" \
|
|
--summary "Delete Me Event" \
|
|
--start "${TEST_DATE}T20:00:00" \
|
|
--end "${TEST_DATE}T21:00:00"
|
|
|
|
# Verify it exists
|
|
$SKILL_DIR/scripts/calendar.sh event list --search "Delete Me"
|
|
|
|
# Delete it
|
|
$SKILL_DIR/scripts/calendar.sh event delete --match "Delete Me Event"
|
|
|
|
# Verify it's gone
|
|
$SKILL_DIR/scripts/calendar.sh event list --search "Delete Me"
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Event is created and visible
|
|
- [ ] Delete removes exactly one event
|
|
- [ ] Other events are untouched
|
|
- [ ] `vdirsyncer sync` ran after delete
|
|
|
|
## 21. Event Delete: Cancel Single Occurrence (EXDATE)
|
|
|
|
Test that `--date` cancels one occurrence of a recurring event without deleting the series.
|
|
|
|
```bash
|
|
# Create a recurring event (weekly on Saturday, 4 weeks)
|
|
NEXT_SAT=$(python3 -c "from datetime import date,timedelta; d=date.today(); d+=timedelta((5-d.weekday())%7 or 7); print(d)")
|
|
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "测试用户" \
|
|
--subject "EXDATE Test (Sat)" \
|
|
--summary "EXDATE Test (Sat)" \
|
|
--start "${NEXT_SAT}T10:00:00" \
|
|
--end "${NEXT_SAT}T11:00:00" \
|
|
--rrule "FREQ=WEEKLY;COUNT=4;BYDAY=SA"
|
|
|
|
# Verify it exists
|
|
$SKILL_DIR/scripts/calendar.sh event list --search "EXDATE Test"
|
|
|
|
# Cancel just the first occurrence
|
|
$SKILL_DIR/scripts/calendar.sh event delete --match "EXDATE Test" --date "$NEXT_SAT"
|
|
|
|
# Verify: .ics file still exists (not deleted)
|
|
ls ~/.openclaw/workspace/calendars/home/ | grep -i exdate
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] `event delete --match ... --date ...` prints "Cancelled ... (added EXDATE, series continues)"
|
|
- [ ] `.ics` file still exists in calendar dir
|
|
- [ ] `khal list` no longer shows the cancelled date but shows subsequent Saturdays
|
|
|
|
## 22. Event Delete: Recurring Without --date or --all (Safety Guard)
|
|
|
|
```bash
|
|
# Try to delete the recurring event without --date or --all — should FAIL
|
|
$SKILL_DIR/scripts/calendar.sh event delete --match "EXDATE Test"
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Script exits with error
|
|
- [ ] Error message explains the two options: `--date` or `--all`
|
|
|
|
## 23. Event Delete: Recurring With --all
|
|
|
|
```bash
|
|
# Delete the entire series
|
|
$SKILL_DIR/scripts/calendar.sh event delete --match "EXDATE Test" --all
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] .ics file is removed
|
|
- [ ] `event list --search "EXDATE Test"` shows nothing
|
|
|
|
## 24. Regression: Send Rejects Unknown Addresses
|
|
|
|
Verify that `send` no longer accepts arbitrary email addresses.
|
|
|
|
```bash
|
|
# This MUST fail — raw unknown email should be rejected
|
|
$SKILL_DIR/scripts/calendar.sh send \
|
|
--to "test@example.com" \
|
|
--subject "Regression Test" \
|
|
--summary "Regression Test Event" \
|
|
--start "${TEST_DATE}T10:00:00" \
|
|
--end "${TEST_DATE}T11:00:00" \
|
|
--dry-run
|
|
```
|
|
|
|
**Verify:**
|
|
- [ ] Command exits with error
|
|
- [ ] Error shows "not found in contacts" with available contacts list
|
|
- [ ] No ICS generated
|
|
|
|
---
|
|
|
|
## Quick Health Checks
|
|
|
|
Run these first if any step fails.
|
|
|
|
```bash
|
|
# icalendar library is available
|
|
uv run --project $SKILL_DIR python -c "import icalendar; print('ok')"
|
|
|
|
# himalaya can list emails
|
|
himalaya envelope list --page-size 5
|
|
|
|
# vdirsyncer can sync
|
|
vdirsyncer sync
|
|
|
|
# khal can read local calendar
|
|
khal list today 7d
|
|
|
|
# todoman can list todos
|
|
todo list
|
|
```
|
|
|
|
## Common Failures
|
|
|
|
| Symptom | Likely Cause |
|
|
|---------|-------------|
|
|
| `himalaya message send` errors | SMTP config issue, check `~/.config/himalaya/config.toml` |
|
|
| No `.ics` attachment found | Email doesn't have a calendar invite, or himalaya can't download attachments |
|
|
| `vdirsyncer sync` fails | Check credentials in `~/.config/vdirsyncer/config`, or server is unreachable |
|
|
| `ModuleNotFoundError: icalendar` | Run `uv sync --project $SKILL_DIR` to install dependencies |
|
|
| Invite shows as attachment (no Accept/Decline) | Check MIME `Content-Type` includes `method=REQUEST` |
|
|
| Event not in `khal list` after sync | Check `.ics` file exists in `~/.openclaw/workspace/calendars/home/` |
|
|
| `todo` command not found | Install with `uv tool install todoman` |
|
|
| `todo list` errors | Check `~/.config/todoman/config.py` exists and `path` points to tasks dir |
|
|
| Todo not syncing | Check `~/.openclaw/workspace/calendars/tasks/` exists, verify vdirsyncer `cal/tasks` pair |
|
|
| DTSTART/BYDAY mismatch error | `--start` date doesn't fall on the BYDAY day. Change the start date to match |
|
|
| Recurring events on wrong day | DTSTART was not aligned with BYDAY. Delete the event and resend with correct `--start` |
|
|
| SMTP rate limit / EOF error | Too many sends too fast. Wait 10+ seconds between sends (Migadu limit) |
|
|
| Events disappeared after cleanup | **Never use `rm *.ics`** on calendar dirs. Use `event delete --match` instead |
|
|
| Recurring series deleted when cancelling one date | Use `--date YYYY-MM-DD` to add EXDATE, not bare `event delete` (which requires `--all` for recurring) |
|
|
| `send` rejects email address | Address not in contacts. Add with `contacts.sh add` first (separate from send) |
|
|
| `send` says "has multiple emails" | Contact has work+home emails. Use `name:type` syntax (e.g. `小橘子:work`) |
|
|
| Contacts dir empty after sync | Check vdirsyncer CardDAV pair is configured for `contacts/default/` |
|