Skill: tdavis009/date-night (version 1.4.0)

--- name: date-night description: > Your AI date night concierge — plans, books, and coordinates an entire evening out through browser automation. Say "plan a date night" and it handles everything: restaurant reservations (OpenTable, Resy), movie tickets (Fandango, Megaplex, AMC), event tickets with price comparison (SeatGeek, Ticketmaster, StubHub), weather checks, drive-time estimates, budget totals, calendar events, and partner notifications. Configurable dietary preferences, childcare reminders, favorite theaters, and babysitter-rate budgeting. First run walks through a friendly onboarding — after that, just tell it what kind of night you want. Triggers: date night, dinner reservation, book a table, OpenTable, Resy, find restaurants, movie tickets, what's playing, concert tickets, sports tickets, events near me, dinner and a movie, plan a date, date ideas, cancel reservation, modify reservation, reconfigure date night preferences. metadata: openclaw: emoji: "💑" requires: bins: - playwright-cli anyBins: - gog - gcal - ical optionalBins: - goplaces - imsg - jq optionalEnv: - GOOGLE_PLACES_API_KEY tools: - web_search - web_fetch - browser - message install: - id: playwright-cli kind: npm package: "@anthropic-ai/playwright-cli@latest" bins: ["playwright-cli"] label: "Install playwright-cli (npm)" - id: chromium kind: shell command: "npx playwright install chromium" label: "Install Chromium for playwright-cli" access: local_data: - path: "~/.openclaw/skills/date-night/config.json" purpose: "User preferences (name, email, phone, dietary, location)" sensitive: true - path: "~/.openclaw/skills/date-night/state/*.json" purpose: "Browser session cookies for Resy (opt-in, clearable)" sensitive: true - path: "~/.openclaw/skills/date-night/history.jsonl" purpose: "Date night log (restaurant, date, rating)" sensitive: false messaging: - channel: "configured notification channel" purpose: "Partner notifications — always drafted and shown for approval before sending" autonomous: false pii: - "Name, email, phone stored locally for auto-filling reservation forms" - "Never transmitted except to booking sites during form submission" sms_read: - purpose: "Retrieve booking verification codes from specific senders only" scope: "Last 1-2 messages from known booking service short codes (e.g. OpenTable 22395)" broad_scan: false always_on: false trigger: "Only during active reservation flow when site sends SMS verification" email_read: - purpose: "Find confirmation numbers for modify/cancel requests only" scope: "Targeted query (e.g. 'from:opentable reservation confirmed'), max 5 results" always_on: false trigger: "Only when user explicitly asks to modify/cancel and lacks confirmation number" --- # Date Night Skill (Published) End-to-end date night planning: restaurants, movies, events, logistics, and notifications. Powered by `playwright-cli` browser automation. --- ## User Preferences This skill uses `~/.openclaw/skills/date-night/config.json`. **Run onboarding on first use** (see below). After that, load config silently at the start of every session. ```bash cat ~/.openclaw/skills/date-night/config.json 2>/dev/null ``` Config schema: ```json { "name": "string", "first_name": "string", "last_name": "string", "user_email": "string", "user_phone": "string (digits only, e.g. 8015550155)", "partner": "string | null", "notify_channel": "iMessage | Telegram | Discord | Signal | SMS", "dietary": ["no alcohol", "vegetarian", "..."], "has_children": false, "children_count": 0, "children_ages": "string | null", "location": "City, ST", "zip": "00000", "preferred_theater": "string | null", "babysitter_rate": 18, "calendar_tool": "gog | gcal | ical", "onboarded_at": "ISO timestamp" } ``` **PII note:** `user_email`, `user_phone`, `first_name`, and `last_name` are used to auto-fill reservation and ticket forms. They are stored locally in `config.json` and never transmitted except to the booking site during form submission. --- ## First Run Onboarding **Check at every skill invocation:** ```bash CONFIG=~/.openclaw/skills/date-night/config.json if [ ! -f "$CONFIG" ]; then echo "ONBOARDING_NEEDED" fi ``` If `ONBOARDING_NEEDED`: run the onboarding flow **before** doing anything else. ### Onboarding Flow Ask questions **one at a time**, conversationally. This is a date night skill — make it feel warm and fun, not like filling out a DMV form. **Opening:** > "Hey! Looks like it's your first time using the Date Night skill — exciting. Let me grab a few quick things so I can make it feel personal. Won't take long." **Question sequence** (ask naturally, wait for each answer): 1. **Name:** > "First — what's your name?" — Collect first name and last name (needed for reservation forms). 2. **Email & Phone:** > "What email and phone number should I use for reservations? These go directly into booking forms — I store them locally and nowhere else." 3. **Partner:** > "Planning these with a partner, or flying solo? (Solo date nights are completely valid.)" — If partner: "What's their name?" 3. **Partner notification:** > "What's the best way to reach {partner}? Like, if I wanted to send them a heads-up about a reservation — iMessage, Telegram, Signal, Discord, something else?" — Skip if solo. 4. **Dietary / lifestyle:** > "Any food preferences or restrictions I should know about? Things like no alcohol, vegetarian, gluten-free, shellfish allergy — or just 'none, we eat everything'?" 5. **Kids:** > "Do you have kids at home? (I ask because I'll remind you about childcare after every booking.)" — If yes: "How many, and roughly what ages?" 6. **Location:** > "What city or zip code are you in? I'll use this for finding nearby restaurants, theaters, and venues." 7. **Theater preference (optional):** > "Any favorite movie theater chain, or a go-to theater near you? I can default to that when searching showtimes. (Skip if you don't have one.)" 8. **Babysitter rate (optional, only if has_children):** > "What's your babysitter rate — roughly? I use it for budget estimates. Default is $18/hr if you're not sure." 9. **Calendar tool:** > "Last one — how do you manage your calendar? I can add events automatically. Options: `gog` (Google), `gcal`, `ical`, or tell me what you use." **Closing:** > "Perfect — you're all set! 🎉 Just say 'plan a date night,' 'find us a restaurant,' or 'get us tickets' anytime. I've got the rest." ### Save Config After collecting all answers, write the file: ```bash mkdir -p ~/.openclaw/skills/date-night cat > ~/.openclaw/skills/date-night/config.json << 'EOF' { "name": "{name}", "first_name": "{first_name}", "last_name": "{last_name}", "user_email": "{email}", "user_phone": "{phone_digits}", "partner": "{partner_or_null}", "notify_channel": "{channel}", "dietary": ["{pref1}", "{pref2}"], "has_children": {true|false}, "children_count": {N}, "children_ages": "{ages_or_null}", "location": "{City, ST}", "zip": "{zip}", "preferred_theater": "{theater_or_null}", "babysitter_rate": {rate}, "calendar_tool": "{tool}", "onboarded_at": "{ISO_TIMESTAMP}" } EOF ``` ### Applying Config Throughout the Skill After loading config, substitute into all references: | Config field | Used in | |-------------|---------| | `first_name` / `last_name` | Auto-fill reservation and ticket forms | | `user_email` | Auto-fill booking forms, account lookups | | `user_phone` | Auto-fill booking forms, SMS verification | | `dietary` | Restaurant recommendations (filter per preferences) | | `location` / `zip` | All nearby searches, drive time estimates | | `partner` | Notification drafts, calendar events | | `notify_channel` | How to send partner notifications | | `has_children` | Childcare prompt after every booking | | `babysitter_rate` | Budget estimates | | `preferred_theater` | Default theater for movie searches | | `calendar_tool` | All calendar event creation commands | **Dietary preference enforcement:** If `dietary` includes `"no alcohol"`: never highlight wine lists, cocktail programs, bar scenes, or alcohol features when recommending restaurants or events. Focus on food, ambiance, service. --- ## Reconfigure If user says **"update my date night preferences"**, **"reconfigure date night"**, or **"change my date night settings"**: ```bash # Back up existing config cp ~/.openclaw/skills/date-night/config.json \ ~/.openclaw/skills/date-night/config.backup.json 2>/dev/null || true ``` Then re-run the onboarding flow with current values shown as defaults: > "Sure — let's update your preferences. I'll show what you have now and you can change anything. Hit Enter to keep the current value." After re-running, overwrite config.json. Confirm: > "Updated! Changes are live for the next date night." --- ## Requirements ### External Binaries (must be installed separately) | Binary | Install | Required? | |--------|---------|-----------| | `playwright-cli` | `npm install -g @playwright/cli@latest` | **Yes** | | Chromium | `npx playwright install chromium` | **Yes** (used by playwright-cli) | | `goplaces` | `brew install steipete/tap/goplaces` | Optional — enhanced restaurant search | PATH setup: `export PATH="$HOME/.npm-global/bin:$PATH"` ### Environment Variables | Variable | Purpose | Required? | |----------|---------|-----------| | `GOOGLE_PLACES_API_KEY` | Google Places API for `goplaces` CLI | Only if using `goplaces` | ### OpenClaw Built-in Capabilities Used These are **standard OpenClaw agent tools** — not external dependencies. They require no separate installation; they are available to any OpenClaw agent with the appropriate tool policies enabled. | OpenClaw Tool | Purpose in This Skill | |---------------|----------------------| | `web_search` | Find restaurants, events, movies, reviews (public web) | | `web_fetch` | Extract menus, reviews, showtimes from URLs (public web) | | `message` (send) | Draft partner notifications — **always shown for approval before sending** | | Calendar skills (`gog`/`ical`) | Create calendar events after booking | | SMS/iMessage skill (`imsg`) | Retrieve booking verification codes (see Sensitive Access below) | **No additional credentials or API tokens are needed** for these built-in tools — they use whatever channels and connectors the user has already configured in their OpenClaw instance. ### Sensitive Access Declaration **SMS verification codes:** OpenTable and Resy send 6-digit verification codes via SMS during booking. This skill retrieves them by reading the **most recent 1–2 messages from the specific sender** (e.g., short code `22395` for OpenTable). It does NOT perform broad inbox scans. Specific patterns used: ```bash # OpenTable: read last message from known short code only imsg history --chat-id {OPENTABLE_CHAT_ID} --limit 1 | grep -oE '[0-9]{6}' # Resy: read last 10 messages, filter for "resy" or "verification" only imsg history --limit 10 | grep -i "resy\|verification\|code" ``` **Gmail (modify/cancel only):** When the user explicitly asks to modify or cancel a reservation and doesn't have the confirmation number, the skill searches Gmail with a **targeted query** — e.g., `from:opentable reservation confirmed` — limited to 5 results. This is never triggered during normal booking flows. **Partner notifications:** Notifications are always **drafted and shown to the user for approval** before sending. The skill never sends messages autonomously. The messaging channel (iMessage/Telegram/Discord/Signal) is whatever the user configures in their OpenClaw instance — no additional credentials are stored by this skill. **Auth state persistence:** The Resy flow optionally saves browser session state to `~/.openclaw/skills/date-night/state/resy-auth.json` to avoid re-login. This file contains session cookies. To clear: `rm -rf ~/.openclaw/skills/date-night/state/`. The skill **never asks for or stores site passwords** — it uses interactive browser login and saves only the resulting session cookies with user consent. ### Data Persistence | Path | Contents | Sensitive? | |------|----------|------------| | `~/.openclaw/skills/date-night/config.json` | Preferences, name, email, phone | **Yes — PII** | | `~/.openclaw/skills/date-night/history.jsonl` | Date night log (restaurant, date, rating) | Low | | `~/.openclaw/skills/date-night/state/*.json` | Browser session cookies (Resy only, opt-in) | **Yes — auth tokens** | To purge all skill data: `rm -rf ~/.openclaw/skills/date-night/` ### Pre-Flight Check ```bash # Verify playwright-cli is available export PATH="$HOME/.npm-global/bin:$PATH" playwright-cli --version || echo "INSTALL: npm install -g @playwright/cli@latest" ``` --- ## References | Topic | File | |-------|------| | Playwright CLI | [references/playwright-cli.md](references/playwright-cli.md) | | OpenTable flow | [references/opentable-flow.md](references/opentable-flow.md) | | Resy flow | [references/resy-flow.md](references/resy-flow.md) | | Restaurant search | [references/search-restaurants.md](references/search-restaurants.md) | | Movie booking | [references/movie-booking.md](references/movie-booking.md) | | Event tickets | [references/event-tickets.md](references/event-tickets.md) | | Finding movies | [references/search-movies.md](references/search-movies.md) | | Finding events | [references/search-events.md](references/search-events.md) | | Modify / Cancel | [references/modify-cancel.md](references/modify-cancel.md) | | SMS verification | [references/sms-codes.md](references/sms-codes.md) | | Pre-evening intel | [references/pre-evening.md](references/pre-evening.md) | | Smart features | [references/smart-features.md](references/smart-features.md) | --- ## Quick Flow: Dinner Reservation ```bash export PATH="$HOME/.npm-global/bin:$PATH" CONFIG=$(cat ~/.openclaw/skills/date-night/config.json) ``` 1. **Find restaurant** — see [search-restaurants.md](references/search-restaurants.md) 2. **Open restaurant page:** ```bash playwright-cli open "https://www.opentable.com/r/{slug}?covers={N}&dateTime={YYYY-MM-DDTHH:MM}" --headed ``` 3. **Snapshot → select date → select time** 4. **Fill guest details** (name, phone, email from user's MEMORY.md or config) 5. **Submit → handle phone verification** (see [sms-codes.md](references/sms-codes.md)) 6. **Confirm → post-booking actions** (below) Full flow details: [opentable-flow.md](references/opentable-flow.md) | [resy-flow.md](references/resy-flow.md) --- ## Quick Flow: Movie Tickets 1. Start at preferred theater (from config) or search via Fandango 2. Select movie, showtime, seats 3. ⚠️ **STOP at payment** — show all-in price, confirm with user before purchase 4. Post-purchase actions (below) Full details: [movie-booking.md](references/movie-booking.md) | [search-movies.md](references/search-movies.md) --- ## Quick Flow: Event Tickets **Start with SeatGeek** (best price discovery + Deal Score): ```bash playwright-cli open "https://seatgeek.com/search?q={event}&location={config.location}" --headed ``` Then cross-check Ticketmaster (official) and StubHub (resale). ⚠️ Ticketmaster has aggressive bot protection — see [event-tickets.md](references/event-tickets.md). ⚠️ **STOP at payment** — confirm all-in price with user before purchase. --- ## Full Date Night Flow When user says *"plan a date night around [event/movie/idea]"*: ``` 1. LOAD config.json silently 2. FIND the event/movie → SeatGeek + Ticketmaster + StubHub price comparison 3. PRESENT options + prices to user → get approval to proceed 4. BOOK tickets (with explicit user confirmation at payment) 5. NOTE venue and show time 6. SEARCH restaurants within 1 mile of venue, open before the show 7. SUGGEST 2-3 dinner options with ratings and OpenTable availability 8. BOOK dinner (with approval) at time that ends ~1 hr before show 9. CHECK weather for that evening → include in summary 10. CALCULATE timeline: leave home → dinner → venue → showtime 11. ADD both events to calendar ({config.calendar_tool}) 12. DRAFT partner notification → show draft → send with approval 13. IF has_children: PROMPT about childcare — every time, no exceptions 14. OFFER dessert spot near venue (optional extension) 15. PRESENT budget estimate: dinner + tickets + babysitter total ``` **Example timeline for 7:30 PM show:** ``` 6:00 PM Leave home 6:30 PM Dinner (2-block walk from venue) 8:00 PM Walk to venue 7:30 PM Show 10:30 PM Done — dessert optional ``` --- ## Post-Booking Actions Execute after **every** reservation or ticket purchase: ### 1. Add to Calendar ```bash {config.calendar_tool} calendar create primary \ --summary "{Event/Dinner} @ {Venue/Restaurant}" \ --from "{datetime}" \ --to "{datetime+duration}" \ --location "{address}" \ --description "{details + confirmation number}" \ --reminder "popup:2h" --reminder "popup:1d" ``` ### 2. Notify Partner (if configured) Draft in user's casual voice → show draft → **require approval before sending**. ``` "Hey {partner}, got us a reservation at {Restaurant} on {date} at {time} 🍽️" ``` ### 3. Childcare Prompt If `has_children: true` — **ALWAYS ask, no exceptions:** > "Do you have childcare sorted for that evening? {N} kid(s) to account for." ### 4. Log to History ```bash mkdir -p ~/.openclaw/skills/date-night cat >> ~/.openclaw/skills/date-night/history.jsonl << 'EOF' {"date":"{YYYY-MM-DD}","restaurant":"{name}","event":"{event_or_null}","total_cost":null,"rating":null,"would_return":null,"weather":null,"notes":""} EOF ``` --- ## Error Recovery | Error | Recovery | |-------|----------| | Config not found | Run onboarding before proceeding | | OT timer expired | Restart from restaurant page URL | | Verification code not received | Wait 30s, click Resend, check SMS | | Phone field cleared | Known OT bug — re-fill before submit | | TM queue/CAPTCHA | Document for user; offer manual assist | | Seat map won't load | Use `playwright-cli screenshot` to diagnose | | StubHub price changed | Re-check before submitting; prices fluctuate | --- ## Checklists ### Before Booking (Any) - [ ] Config loaded - [ ] Restaurant/movie/event, date, time, party size confirmed with user - [ ] **Full price shown to user before any purchase** ### After Booking (Any) - [ ] Calendar event created - [ ] Partner notified (draft approved) - [ ] Childcare asked (if has_children) - [ ] Logged to history - [ ] Pre-evening intel offered (weather, parking, drive time) - [ ] Budget estimate presented

path: config.example.json
content: {
  "name": "Your Name",
  "first_name": "First",
  "last_name": "Last",
  "user_email": "[REDACTED_SECRET]",
  "user_phone": "5551234567",
  "partner": "Partner Name",
  "notify_channel": "iMessage",
  "dietary": [],
  "has_children": false,
  "children_count": 0,
  "children_ages": null,
  "location": "City, ST",
  "zip": "00000",
  "preferred_theater": null,
  "babysitter_rate": 18,
  "calendar_tool": "gog",
  "onboarded_at": null
}


path: references/event-tickets.md
content: # Event Ticket Booking — Detailed Reference

Book concert, sports, comedy, and theater tickets via browser automation.
Covers SeatGeek (recommended), Ticketmaster, and StubHub.

Load config: `CONFIG=$(cat ~/.openclaw/skills/date-night/config.json)`

All commands assume PATH is set: `export PATH="$HOME/.npm-global/bin:$PATH"`

---

## ⚠️ Payment Warning

Event tickets are typically non-refundable. Always:
1. Show the **all-in price** (face value + service fees + facility charges + order processing) before any action
2. **Explicitly confirm with user** before clicking any "Purchase" button
3. Communicate clearly: *"Event tickets are usually non-refundable. Just confirming you want to proceed."*

---

## Platform Overview

| Platform | Type | Best For | Bot Protection | Guest Checkout |
|----------|------|----------|----------------|----------------|
| SeatGeek | Aggregator + resale | Price discovery, Deal Scores | 🟢 Low | ✅ Yes |
| Ticketmaster | Primary ticketer | Official tickets, major venues | 🔴 Very High | ✅ Limited |
| StubHub | Resale only | Sold-out events, last-minute | 🟡 Medium | ✅ Yes |

**Recommended flow:** Start SeatGeek → compare Ticketmaster + StubHub → purchase best option.

---

## SeatGeek (Start Here)

**Website:** seatgeek.com | **Type:** Aggregator + resale

### URL Patterns
```
# City events
https://seatgeek.com/{city-name}-tickets
https://seatgeek.com/venues/{city-name}/tickets

# Search
https://seatgeek.com/search?q={query}&location={city}%2C+{state}

# Performer
https://seatgeek.com/{performer-slug}-tickets

# Venue
https://seatgeek.com/venues/{venue-slug}/tickets
```

### Booking Flow
```bash
playwright-cli -s=sg open "https://seatgeek.com/search?q={event}&location={config.location}" --headed
playwright-cli -s=sg snapshot
playwright-cli -s=sg click {event-ref}
playwright-cli -s=sg snapshot

# Browse venue map — click section for prices
playwright-cli -s=sg click {section-ref}
playwright-cli -s=sg snapshot
# See: row, seats, price/ticket, Deal Score, view-from-seat (sometimes)

playwright-cli -s=sg click {ticket-listing-ref}
playwright-cli -s=sg snapshot
# ⚠️ STOP — show all-in total (SeatGeek shows fees upfront)
```

After approval:
```bash
playwright-cli -s=sg click {checkout-ref}
playwright-cli -s=sg snapshot
playwright-cli -s=sg fill {email-ref} "{config.user_email}"
playwright-cli -s=sg click {continue-as-guest-ref}
# Stop at payment — wait for user's authorization
```

**Deal Score:** SeatGeek's 1–100 value rating. 70+ = good deal, 90+ = exceptional. Mention it when selecting.

**Fees:** SeatGeek shows all-in prices by default — what you see is what you pay.

---

## Ticketmaster

**Website:** ticketmaster.com | **Type:** Primary ticketer

### ⚠️ Honest Limitations

Ticketmaster actively fights automation:
- **Queue system:** Major events place users in a virtual waiting room; automation gets blocked or deprioritized
- **Verified Fan:** Pre-registration + unique access codes for high-demand acts — cannot automate
- **CAPTCHA:** Frequent, especially on rapid interactions — requires manual solving
- **Fingerprinting:** Detects headless browsers, unusual click patterns, rapid navigation
- **Session timeouts:** Ticket holds expire in ~8–10 minutes

**Realistic expectations:**
- ✅ Works: browsing events, checking prices, event discovery
- ⚠️ Difficult: seat selection for popular events (queue/CAPTCHA)
- ❌ Not reliable: high-demand presales, Verified Fan sales

**When TM automation stalls:** Offer to do the work in headed mode while user monitors, or give user the direct event URL to complete manually.

### URL Patterns
```
https://www.ticketmaster.com/search?q={query}
https://www.ticketmaster.com/{city}-tickets
https://www.ticketmaster.com/discover/concerts/{state}
https://www.ticketmaster.com/{artist-slug}/artist/{id}
https://www.ticketmaster.com/{venue-slug}/venue/{id}
https://www.ticketmaster.com/event/{event-id}
```

### Booking Flow (When Accessible)
```bash
playwright-cli -s=tm open "https://www.ticketmaster.com/search?q={event}" --headed
# Wait 3+ seconds for SPA to load
playwright-cli -s=tm snapshot
playwright-cli -s=tm click {event-ref}
playwright-cli -s=tm snapshot

# Check for queue
# If queue appears: STOP. Notify user. Provide event URL.
# If no queue: proceed
playwright-cli -s=tm click {section-ref}
playwright-cli -s=tm snapshot
playwright-cli -s=tm click {add-to-cart-ref}
playwright-cli -s=tm snapshot

# ⚠️ STOP — show price (TM fees typically +25-35% of face value)
# Report: face value + service fee + facility charge + order processing = total
```

**Fees:** Ticketmaster fees are notoriously high (25–35% above face value). Always report the all-in total before user approves.

**Upsells:** TM will offer ticket insurance (~$5–15) and mobile wallet. Ask user if they want these.

---

## StubHub

**Website:** stubhub.com | **Type:** Resale marketplace

### URL Patterns
```
https://www.stubhub.com/search?q={query}
https://www.stubhub.com/{city}-tickets/
https://www.stubhub.com/concert-tickets/
https://www.stubhub.com/sports-tickets/
https://www.stubhub.com/venue/{venue-slug}/events
```

### Booking Flow
```bash
playwright-cli -s=sh open "https://www.stubhub.com/search?q={event}+{city}" --headed
playwright-cli -s=sh snapshot
playwright-cli -s=sh click {event-ref}
playwright-cli -s=sh snapshot

# Table of listings: section, row, quantity, price
# Sort by price or "Best Value"
playwright-cli -s=sh click {sort-ref}
playwright-cli -s=sh snapshot
playwright-cli -s=sh click {listing-ref}
playwright-cli -s=sh snapshot

# ⚠️ STOP — show full price (StubHub adds ~10-15% buyer premium at checkout)
# Enable "all-in pricing" toggle if available to see true total upfront
```

**Key caveats for resale:**
- Prices fluctuate by the hour — note this to user
- Buyer premium is ~10–15% on top of listing price
- FanProtect guarantee: if tickets are invalid, StubHub provides replacement or refund
- Delivery: most are instant mobile transfer; some seller transfers take hours

---

## Cross-Platform Price Comparison

```bash
export PATH="$HOME/.npm-global/bin:$PATH"
EVENT="{event name}"
LOCATION="{config.location}"

playwright-cli -s=sg open "https://seatgeek.com/search?q=${EVENT}&location=${LOCATION}" --headed &
playwright-cli -s=tm open "https://www.ticketmaster.com/search?q=${EVENT}" --headed &
playwright-cli -s=sh open "https://www.stubhub.com/search?q=${EVENT}+${LOCATION}" --headed &
wait

playwright-cli -s=sg snapshot
playwright-cli -s=tm snapshot
playwright-cli -s=sh snapshot
```

**Comparison output:**
```
💰 **{Event} — Ticket Price Comparison**
📅 {Date} @ {Venue}

🟢 SeatGeek (aggregator)
   Best: ${price}/ticket all-in | Deal Score: {N}/100

🔵 Ticketmaster (official)
   Face: ${price} + ${fees} in fees = ${total}/ticket
   {Note any queue or availability issues}

🟠 StubHub (resale)
   Lowest: ${price} + ~15% premium = ${total}/ticket

📊 Recommendation: {platform} because {reason}
```

---

## Post-Purchase Actions

1. **Calendar:**
```bash
{config.calendar_tool} calendar create primary \
  --summary "{Event} @ {Venue}" \
  --from "{datetime}" \
  --to "{datetime + estimated duration}" \
  --location "{venue address}" \
  --description "Section: {section}, Row: {row}\nConfirmation: {num}\nPlatform: {platform}\nAll-in paid: ${total}" \
  --reminder "popup:3h" --reminder "popup:1d"
```

2. **Partner notification** (draft → approve → send via `config.notify_channel`)

3. **Childcare prompt** (if `has_children` — mandatory, every time)

4. **Offer pre-show dinner:**
> "Want me to find a restaurant near {Venue} for pre-show dinner? The show starts at {time}, so dinner around {time minus 2.5h} would work well."

5. **Parking note:**
   - Search `web_search "{venue name} parking options prepay"` for venue-specific parking
   - SpotHero and ParkWhiz offer prepay parking near many major venues


path: references/modify-cancel.md
content: # Modify & Cancel Reservations — Detailed Reference

How to modify or cancel existing reservations on OpenTable and Resy.

Load config: `CONFIG=$(cat ~/.openclaw/skills/date-night/config.json)`

All commands assume PATH is set: `export PATH="$HOME/.npm-global/bin:$PATH"`

---

## OpenTable: Modify Reservation

### Via Confirmation Email (Easiest)
OpenTable sends a confirmation email with direct "Modify" and "Cancel" links:
```bash
# Search for confirmation email (if using gog/Gmail)
gog gmail search "from:opentable reservation confirmed" --max 5 2>/dev/null || true
```

### Via Website
```bash
playwright-cli -s=modify open "https://www.opentable.com/my/reservations" --headed
playwright-cli -s=modify snapshot
# Find reservation by confirmation # + last name (no login required):
playwright-cli -s=modify click {find-reservation-ref}
playwright-cli -s=modify fill {confirmation-ref} "{confirmation_number}"
playwright-cli -s=modify fill {last-name-ref} "{config.last_name}"
playwright-cli -s=modify click {find-ref}
```

### Modification Steps
```bash
playwright-cli -s=modify click {modify-ref}
playwright-cli -s=modify snapshot
# Change: date, time, or party size
playwright-cli -s=modify click {new-date-ref}
playwright-cli -s=modify click {new-time-ref}
playwright-cli -s=modify click {save-changes-ref}
```

**After modifying:**
1. Update the calendar event
2. Notify partner of the change (draft → approve → send)

---

## OpenTable: Cancel Reservation

```bash
playwright-cli -s=cancel open "https://www.opentable.com/my/reservations" --headed
playwright-cli -s=cancel snapshot
playwright-cli -s=cancel click {cancel-ref}
playwright-cli -s=cancel click {confirm-cancel-ref}
```

**Post-cancellation:**
1. Delete the calendar event
2. Notify partner if they were told about it (draft → approve → send)
3. Update history entry: set `"cancelled": true`

---

## Resy: Cancel Reservation

Resy requires login. Load saved auth if available:
```bash
playwright-cli -s=resy-cancel state-load ~/.openclaw/skills/date-night/state/resy-auth.json 2>/dev/null
playwright-cli -s=resy-cancel open "https://resy.com/account/reservations" --headed
playwright-cli -s=resy-cancel snapshot
playwright-cli -s=resy-cancel click {reservation-ref}
playwright-cli -s=resy-cancel click {cancel-ref}
playwright-cli -s=resy-cancel click {confirm-ref}
```

**⚠️ Resy Cancellation Policies:**
Many Resy restaurants have strict policies:
- 24–48 hour cancellation windows
- No-show fees: $25–100+ per person
- Prepaid/ticketed reservations may be non-refundable

Always check the policy and remind user of the deadline:
> "This reservation has a 24-hour cancellation policy. You need to cancel by {deadline} to avoid a fee."

## Resy: "Modify" (Cancel + Rebook)

Resy does not support direct modification. To change details:
1. Book new reservation first (if possible)
2. Cancel original after new one is confirmed
3. **Warn user:** popular restaurants may not have availability for new time

---

## Finding Reservation Details

If user doesn't have confirmation number:

```bash
# Check history file
cat ~/.openclaw/skills/date-night/history.jsonl 2>/dev/null | \
  jq -s 'sort_by(.date) | reverse | .[0:5]' 2>/dev/null || true

# Search email — targeted to booking senders only
gog gmail search "from:opentable OR from:resy subject:reservation confirmed" --max 5 2>/dev/null || true
```

---

## Movie / Event Ticket Cancellations

**Important:** Movie and event tickets are generally **non-refundable**. Always communicate this before purchase.

**Exceptions to check:**
- Some chains (Cinemark, Fandango) allow cancellation up to 1 hour before showtime for a partial refund
- SeatGeek / StubHub FanProtect covers invalid tickets but not buyer's remorse
- Ticketmaster "ticket insurance" (sold at checkout) may cover cancellation — check if user purchased it

To check cancellation options:
```bash
web_search "{platform} cancel movie tickets policy"
web_search "{chain} refund policy online tickets"
# Then navigate to the platform's order management page
```


path: references/movie-booking.md
content: # Movie Ticket Booking — Detailed Reference

Book movie tickets via browser automation using `playwright-cli`.
Covers the major national chains; use `config.preferred_theater` as the default starting point.

Load config: `CONFIG=$(cat ~/.openclaw/skills/date-night/config.json)`

All commands assume PATH is set: `export PATH="$HOME/.npm-global/bin:$PATH"`

---

## ⚠️ Payment Warning

Always:
1. Show the **full all-in price** (tickets + booking fees + taxes) before proceeding
2. **Explicitly confirm with user** before clicking any "Purchase" / "Place Order" button
3. Never auto-complete a ticket purchase

---

## Finding Your Local Theater

Use `config.preferred_theater` and `config.zip` to determine where to start:

```bash
ZIP=$(cat ~/.openclaw/skills/date-night/config.json | jq -r '.zip')
PREF=$(cat ~/.openclaw/skills/date-night/config.json | jq -r '.preferred_theater // "none"')
```

If no preference set:
```bash
web_search "movie theaters near {config.zip} showtimes"
playwright-cli -s=fd open "https://www.fandango.com/movies-in-theaters?location=${ZIP}" --headed
playwright-cli -s=fd snapshot
```

Fandango is the best aggregator for finding all theaters near a zip code.

---

## Common Theater Chains

### Megaplex Theatres (Utah / Nevada)
- **Website:** megaplex.com
- **Loyalty:** MegaClub (free, earns free tickets)
- **Guest checkout:** ✅ Available; account recommended for points
- **Theater URL pattern:** `megaplex.com/{theater-slug}` (e.g., `megaplex.com/legacycrossing`)
- **Find locations:** `megaplex.com/theatres/locations`

```bash
playwright-cli -s=movies open "https://megaplex.com/{theater-slug}" --headed
playwright-cli -s=movies snapshot
# Click movie → click showtime → seat map → checkout
```

### AMC Theatres (National)
- **Website:** amctheatres.com
- **Loyalty:** AMC Stubs (free) / A-List ($25/mo, 3 movies/week)
- **Guest checkout:** Limited — account preferred for seat selection
- **Showtimes:** `amctheatres.com/theatres/{theater-slug}/showtimes`
- **Find nearby:** `amctheatres.com/movies` → select theater

```bash
playwright-cli -s=amc open "https://www.amctheatres.com/movies" --headed
playwright-cli -s=amc snapshot
playwright-cli -s=amc click {movie-ref}
playwright-cli -s=amc click {showtime-ref}
# Ticket type selection (adult/child/senior) → seat map → checkout
```

AMC charges a per-ticket online booking fee (~$1.50–2.50/ticket). Always include in price shown to user.

### Cinemark (National)
- **Website:** cinemark.com
- **Loyalty:** Movie Club ($8.99/mo, 1 ticket/month)
- **Guest checkout:** ✅ Best guest checkout of major chains
- **Showtimes:** Theater finder on homepage → select by zip

```bash
playwright-cli -s=cin open "https://www.cinemark.com" --headed
playwright-cli -s=cin snapshot
# Theater search by zip → movie → showtime → ticket count → seat map → guest checkout
playwright-cli -s=cin fill {email-ref} "{config.user_email}"
playwright-cli -s=cin click {guest-checkout-ref}
```

### Regal Cinemas (National)
- **Website:** regmovies.com
- **Loyalty:** Regal Crown Club (free) / Unlimited ($20+/mo)
- **Guest checkout:** ✅ Available
- **Showtimes near zip:** `regmovies.com/movies/now-playing?distance=25&zip={zip}`

```bash
playwright-cli -s=reg open "https://www.regmovies.com/movies/now-playing?distance=25&zip={config.zip}" --headed
playwright-cli -s=reg snapshot
playwright-cli -s=reg click {movie-ref}
playwright-cli -s=reg click {showtime-ref}
# Seat selection → checkout
```

---

## Generic Booking Flow

Works for all chains with minor variation:

**Step 1: Navigate to theater / showtime**
```bash
playwright-cli -s=movies open "{theater-url}" --headed
playwright-cli -s=movies snapshot
```

**Step 2: Select movie and showtime**
```bash
playwright-cli -s=movies click {movie-ref}
playwright-cli -s=movies snapshot
playwright-cli -s=movies click {showtime-ref}
playwright-cli -s=movies snapshot
```

**Step 3: Seat selection (interactive map)**
```bash
playwright-cli -s=movies snapshot
# Seat map: green=available, gray=taken, yellow=selected
playwright-cli -s=movies click {seat-ref}     # first seat
playwright-cli -s=movies click {seat-ref-2}   # second seat (party of 2)
playwright-cli -s=movies snapshot             # verify selection
playwright-cli -s=movies click {continue-ref}
```

**Seat map troubleshooting:**
- SVG/canvas seats may not expose aria refs — try coordinate clicking:
  ```bash
  playwright-cli mousemove {x} {y}
  playwright-cli mousedown
  playwright-cli mouseup
  ```
- If map doesn't render: `playwright-cli screenshot` to diagnose
- Query seat count: `playwright-cli eval "document.querySelectorAll('[data-seat-status=available]').length"`

**Step 4: ⚠️ STOP — Confirm price before checkout**

```bash
playwright-cli -s=movies snapshot
# Capture: ticket count, price per ticket, booking fee, tax, total
```

Present to user:
> "2 tickets to {Movie} on {Date} at {Time}, {Theater}. Total: ${amount} (includes ${fee} booking fee). Shall I proceed?"

**Step 5: Complete checkout (after approval)**
```bash
playwright-cli -s=movies fill {email-ref} "{config.user_email}"
playwright-cli -s=movies click {place-order-ref}
playwright-cli -s=movies snapshot
# Capture confirmation number
```

---

## Seat Selection Guide

| Location | Best For |
|----------|----------|
| Center, rows 7–12 of ~20 | Ideal date night — balanced view, good audio |
| Upper-center | Premium formats (IMAX/PLF) — avoid front rows |
| Aisle | Good for restroom access |
| Row 1–3 | Avoid — neck strain, especially in IMAX |

**For a date night:** Center-middle, slightly upper. Avoid splitting into separated seats.

---

## Post-Purchase Actions

1. **Calendar:**
```bash
{config.calendar_tool} calendar create primary \
  --summary "{Movie} @ {Theater}" \
  --from "{datetime}" \
  --to "{datetime + runtime + 30min}" \
  --location "{theater name and address}" \
  --description "Seats: {section/row/numbers}\nConfirmation: {num}\nFormat: {standard/IMAX}" \
  --reminder "popup:2h"
```

2. **Partner notification** (draft → approve → send via `config.notify_channel`)

3. **Childcare prompt** (if `config.has_children: true` — mandatory):
> "Do you have childcare sorted for that evening? {N} kid(s) to account for."

4. **Offer dinner near theater:**
> "Want me to find a restaurant near {Theater} for before the movie?"


path: references/opentable-flow.md
content: # OpenTable Booking Flow — Detailed Reference

Step-by-step browser automation for booking on OpenTable using `playwright-cli`.

Before starting, load config: `cat ~/.openclaw/skills/date-night/config.json`

All commands assume PATH is set: `export PATH="$HOME/.npm-global/bin:$PATH"`

---

## 1. Restaurant Page URL

```
https://www.opentable.com/r/{slug}?covers={party_size}&dateTime={YYYY-MM-DDTHH:MM}
```

**Finding the slug:** Search `https://www.opentable.com/s?term=RESTAURANT+NAME` or web search `site:opentable.com "{restaurant name}" "{city}"`.

## 2. Open & Initial Snapshot

```bash
playwright-cli -s=reservation open "https://www.opentable.com/r/{slug}?covers={N}&dateTime={YYYY-MM-DDTHH:MM}" --headed
playwright-cli -s=reservation snapshot
```

Look for: time slot buttons (pre-filled), "Find a time" / "View full availability" button, or "No availability" message.

## 3. Availability Modal

```bash
playwright-cli -s=reservation click {availability-button-ref}
playwright-cli -s=reservation snapshot
```

Shows: calendar, time slot grid once a date is selected, party size selector.

## 4. Date Selection

```bash
playwright-cli -s=reservation snapshot
# If target month not visible:
playwright-cli -s=reservation click {next-month-arrow-ref}
playwright-cli -s=reservation snapshot
playwright-cli -s=reservation click {date-ref}
```

## 5. Time Selection

Time slots render as `<a>` links, not buttons:

```bash
playwright-cli -s=reservation snapshot
playwright-cli -s=reservation click {time-ref}
```

If exact time unavailable: snapshot shows all available times. Pick nearest, **confirm with user before proceeding**.

## 6. Checkout — Fill Guest Details

```bash
playwright-cli -s=reservation snapshot
playwright-cli -s=reservation fill {phone-ref} "{config.user_phone}"
playwright-cli -s=reservation fill {email-ref} "{config.user_email}"
playwright-cli -s=reservation fill {first-name-ref} "{config.first_name}"
playwright-cli -s=reservation fill {last-name-ref} "{config.last_name}"
```

`fill` handles React controlled inputs natively.

**Dietary preferences:** If config has dietary restrictions, fill the Special Requests field:
```bash
playwright-cli -s=reservation fill {special-requests-ref} "{dietary-note}"
```

## 7. Occasion & Special Requests

**Occasion dropdown** (React custom — use click, not select):
```bash
playwright-cli -s=reservation click {occasion-dropdown-ref}
playwright-cli -s=reservation snapshot
playwright-cli -s=reservation click {option-ref}   # "Date Night", "Anniversary", etc.
```

**Special requests textarea:**
```bash
playwright-cli -s=reservation fill {special-requests-ref} "Booth seating preferred"
```

## 8. Submit Reservation

```bash
playwright-cli -s=reservation click {complete-reservation-ref}
```

Triggers either: phone verification modal, or confirmation page (if phone already verified in session).

## 9. Phone Verification (Cross-Origin Iframe)

The verification input is in a cross-origin iframe — use `press`, not `fill`.

```bash
# Wait for SMS
sleep 5
# Retrieve code — see sms-codes.md for {CHAT_ID}
imsg history --chat-id {OPENTABLE_CHAT_ID} --limit 1 2>/dev/null | grep -oE '[0-9]{6}'

# Type each digit
playwright-cli -s=reservation press {d1}
playwright-cli -s=reservation press {d2}
playwright-cli -s=reservation press {d3}
playwright-cli -s=reservation press {d4}
playwright-cli -s=reservation press {d5}
playwright-cli -s=reservation press {d6}
```

## 10. Post-Verification Phone Fix

**Known OpenTable bug:** Verification often clears the phone field. After verification:

```bash
playwright-cli -s=reservation snapshot
playwright-cli -s=reservation fill {phone-ref} "{config.user_phone}"
playwright-cli -s=reservation click {complete-reservation-ref}
```

## 11. Confirmation

```bash
playwright-cli -s=reservation snapshot
```

Capture: restaurant name, date, time, party size, confirmation number. Then execute Post-Booking Actions in SKILL.md.

---

## Timer Expiry Recovery

OpenTable holds a table for ~**10 minutes** after time slot selection. If expired:
1. Go back to the restaurant URL
2. Reselect a time slot (may no longer be available)
3. Move quickly through checkout — gather all info upfront before starting

**Prevention:** Have phone, email, name ready before opening the page. Don't pause mid-checkout.

---

## Verification Failures

| Issue | Solution |
|-------|----------|
| No code after 30s | Click "Resend code". Wait. Check SMS again. |
| Code expired (10 min) | Click "Resend code" for a fresh one. |
| Wrong code | `press Backspace` × 6, re-enter. |
| Modal disappeared | Snapshot — may need to click "Complete reservation" again. |


path: references/playwright-cli.md
content: # Playwright CLI Command Reference

`playwright-cli` is Microsoft's CLI for AI browser automation — more token-efficient than raw Playwright or MCP because it keeps browser state external and returns only what's needed.

## Installation & PATH

```bash
# Already installed at:
~/.npm-global/bin/playwright-cli

# Ensure PATH in every exec call:
export PATH="$HOME/.npm-global/bin:$PATH"
```

## Key Concepts

### Element Refs
- `snapshot` returns compact YAML with element refs like `e8`, `e21`, `e47`
- Use these refs in `click`, `fill`, `hover`, `select`, etc.
- Refs are valid only until the next page navigation or major DOM change
- **Always snapshot before interacting** — stale refs will fail

### Sessions
- Default session persists cookies/storage in memory (lost on browser close)
- Use `-s=NAME` for named sessions to isolate different tasks
- Use `--persistent` to save profile to disk across browser restarts

## Core Commands

### Browser Lifecycle

```bash
# Open browser (headless by default)
playwright-cli open [url]

# Open in headed mode (visible browser window)
playwright-cli open "https://example.com" --headed

# Navigate to URL (in existing session)
playwright-cli goto "https://example.com"

# Close the page
playwright-cli close
```

### Page Inspection

```bash
# Capture page snapshot — returns YAML with element refs
playwright-cli snapshot

# Save snapshot to file
playwright-cli snapshot --filename=page.yaml

# Take screenshot (PNG)
playwright-cli screenshot

# Screenshot a specific element
playwright-cli screenshot e12

# Evaluate JavaScript on page
playwright-cli eval "document.title"

# Evaluate JS on a specific element
playwright-cli eval "el => el.textContent" e12
```

### Interaction

```bash
# Click an element by ref
playwright-cli click e12

# Right-click
playwright-cli click e12 right

# Double-click
playwright-cli dblclick e12

# Hover over element
playwright-cli hover e12

# Fill a text input (HANDLES REACT CONTROLLED INPUTS)
playwright-cli fill e15 "Hello World"

# Type text into focused/editable element (keystroke by keystroke)
playwright-cli type "Hello World"

# Select dropdown option
playwright-cli select e20 "option-value"

# Check / uncheck checkbox or radio
playwright-cli check e25
playwright-cli uncheck e25

# Upload file
playwright-cli upload "/path/to/file.pdf"

# Drag and drop
playwright-cli drag e10 e20
```

### Keyboard

```bash
# Press a single key
playwright-cli press Enter
playwright-cli press Tab
playwright-cli press Escape
playwright-cli press ArrowDown

# Press a digit (for verification codes in iframes)
playwright-cli press 7
playwright-cli press 1

# Key combinations
playwright-cli press Control+a
playwright-cli press Meta+c

# Key down / key up (for held keys)
playwright-cli keydown Shift
playwright-cli keyup Shift
```

### Mouse (Low-Level)

```bash
playwright-cli mousemove 100 200    # move to coordinates
playwright-cli mousedown            # press left button
playwright-cli mouseup              # release left button
playwright-cli mousewheel 0 -300    # scroll down
playwright-cli mousewheel 0 300     # scroll up
```

### Navigation

```bash
playwright-cli go-back       # browser back
playwright-cli go-forward    # browser forward
playwright-cli reload        # refresh page
```

### Tabs

```bash
playwright-cli tab-list          # list all tabs
playwright-cli tab-new "url"     # open new tab
playwright-cli tab-select 1      # switch to tab by index
playwright-cli tab-close 2       # close tab by index
```

### Dialogs

```bash
playwright-cli dialog-accept              # accept alert/confirm
playwright-cli dialog-accept "response"   # accept prompt with text
playwright-cli dialog-dismiss             # dismiss/cancel dialog
```

### Window

```bash
playwright-cli resize 1280 720    # resize browser window
```

### Session Management

```bash
playwright-cli list          # list all sessions
playwright-cli close-all     # close all browsers gracefully
playwright-cli kill-all      # force-kill all browser processes

# Named sessions
playwright-cli -s=booking open "https://..." --headed
playwright-cli -s=booking snapshot
playwright-cli -s=booking click e12
playwright-cli -s=booking close
```

### Storage & State

```bash
# Save/load authentication state
playwright-cli state-save auth.json
playwright-cli state-load auth.json

# Cookies
playwright-cli cookie-list
playwright-cli cookie-get "session_id"
playwright-cli cookie-set "name" "value"
playwright-cli cookie-delete "name"
playwright-cli cookie-clear

# Local storage
playwright-cli localstorage-list
playwright-cli localstorage-get "key"
playwright-cli localstorage-set "key" "value"
playwright-cli localstorage-delete "key"
playwright-cli localstorage-clear

# Session storage (same pattern)
playwright-cli sessionstorage-list
playwright-cli sessionstorage-get "key"
```

### Network

```bash
# Mock network requests
playwright-cli route "**/*.png" --status 404
playwright-cli route-list
playwright-cli unroute "**/*.png"

# List network requests since page load
playwright-cli network
```

### DevTools & Debugging

```bash
playwright-cli console          # list console messages
playwright-cli console warn     # filter by min level
playwright-cli show             # open visual dashboard
playwright-cli devtools-start   # open browser devtools

# Tracing
playwright-cli tracing-start
playwright-cli tracing-stop

# Video recording
playwright-cli video-start
playwright-cli video-stop
```

### PDF

```bash
playwright-cli pdf    # save current page as PDF
```

## Reservation-Specific Patterns

### Pattern: Fill React Form Fields

`fill` handles React controlled inputs natively — no need for the `nativeSetter` hack:

```bash
playwright-cli snapshot                         # get refs
playwright-cli fill e15 "{config.user_phone}"   # phone
playwright-cli fill e18 "{config.user_email}"   # email
playwright-cli fill e21 "{config.first_name}"   # first name
playwright-cli fill e24 "{config.last_name}"    # last name
```

### Pattern: Cross-Origin Iframe Input (Verification Codes)

Cannot `fill` or `click` inside cross-origin iframes. Use keyboard instead:

```bash
# Code is "714289"
playwright-cli press 7
playwright-cli press 1
playwright-cli press 4
playwright-cli press 2
playwright-cli press 8
playwright-cli press 9
```

### Pattern: Custom React Dropdowns (Occasion)

React dropdowns don't respond to `select`. Instead:

```bash
playwright-cli click e30          # click the dropdown to open it
playwright-cli snapshot           # see the options with refs
playwright-cli click e35          # click the desired option
```

### Pattern: Scroll to Find Elements

If elements are below the fold:

```bash
playwright-cli mousewheel 0 -500   # scroll down
playwright-cli snapshot             # re-snapshot to get new refs
```

### Pattern: Wait for Page Load

After navigation or clicking that triggers a page change:

```bash
playwright-cli click e12            # triggers navigation
# Brief pause to allow page load
playwright-cli snapshot             # will wait for stability before returning
```

## Tips

1. **Always snapshot before interacting** — refs go stale after DOM changes
2. **Use `fill` over `type`** — `fill` clears the field first and triggers React events
3. **Use `press` for iframe inputs** — only keyboard events cross iframe boundaries
4. **Use `--headed` for debugging** — see exactly what the browser sees
5. **Named sessions** prevent interference between concurrent automations
6. **`eval` is the escape hatch** — if snapshot doesn't show what you need, query the DOM directly


path: references/pre-evening.md
content: # Pre-Evening Intelligence — Detailed Reference

Gather and present useful information before a date night: menu preview, dress code, parking, drive time, weather, and dessert spots.

Load config: `CONFIG=$(cat ~/.openclaw/skills/date-night/config.json)`

---

## 1. Menu Preview

### From OpenTable Restaurant Page
```bash
playwright-cli -s=intel open "https://www.opentable.com/r/{slug}" --headed
playwright-cli -s=intel snapshot
playwright-cli -s=intel click {menu-tab-ref}
playwright-cli -s=intel snapshot
```

### From Restaurant Website
```bash
web_search "{restaurant name} {city} menu"
web_fetch "https://restaurant-website.com/menu" --maxChars 5000
```

### Presentation Rules

Apply dietary preferences from config when presenting menus:
- If `dietary` includes `"no alcohol"`: do **not** highlight wine lists, cocktail pairings, bar menus, or happy hour specials. Focus on food, signature dishes, chef specialties.
- If `dietary` includes `"vegetarian"`: highlight plant-based options explicitly.
- If allergies in `dietary`: flag any potential allergen in described dishes.

**Example menu summary:**
> *The restaurant is known for their dry-aged steaks — the bone-in NY strip is a signature. Great sides. Entrées run $45–75.*

---

## 2. Dress Code

### Extract from OpenTable
```bash
playwright-cli -s=intel snapshot
playwright-cli -s=intel mousewheel 0 -500  # scroll to Details section
playwright-cli -s=intel snapshot
# Look for "Dress Code" or "Attire" in snapshot
```

### Extract via Web Search
```bash
web_search "{restaurant name} {city} dress code attire"
```

### Inference by Price Range
| Price | Inferred Dress Code |
|-------|---------------------|
| $$$$ | Business casual to smart casual (minimum) |
| $$$ | Smart casual |
| $$ | Casual — no dress code concern |
| $ | Casual |

### Presentation
> *Dress code: business casual — a button-down and nice pants, or a nice dress.*

---

## 3. Parking & Arrival Info

### Extract from OpenTable / Web
```bash
playwright-cli -s=intel snapshot  # look for "Parking", "Valet", "Transportation"
web_search "{restaurant name} {city} parking valet"
```

### For Event Venues
```bash
web_search "{venue name} parking options"
web_search "{venue name} preferred parking lots"
# Many major venues have pre-pay parking: SpotHero, ParkWhiz
```

### Presentation
> *Parking: valet available ($15). There's also a covered garage one block east.*

---

## 4. Drive Time from Home

Use `config.location` as the origin.

### Calculate
```bash
# Via web search
web_search "drive time from {config.location} to {restaurant address}"

# Via goplaces (if address known)
goplaces search "{restaurant name} {city}" --limit 1 --json 2>/dev/null | \
  jq -r '.[0].formattedAddress' 2>/dev/null || true
```

### "Leave By" Calculation
```
Leave time = Reservation time - Drive time - 15 min buffer
```

Include in brief:
> *It's about {X} minutes from {config.location}. I'd suggest leaving by {time} to have time to park and settle in.*

---

## 5. Weather Check

Check forecast for the evening using the wttr.in service.

```bash
# Quick format: temp, conditions, precip, wind
curl -s "wttr.in/{config.location}?format=%l:+%t+%C+💧%p+💨%w" 2>/dev/null || true

# 3-day summary
curl -s "wttr.in/{config.location}?format=3" 2>/dev/null || true

# URL-encode city: spaces → +, commas → %2C
# Example: "Salt Lake City, UT" → "Salt+Lake+City%2C+UT"
```

### Advisories by Condition

| Condition | Advisory |
|-----------|----------|
| Below freezing | Covered parking recommended; note walking distance |
| Rain / snow | Mention umbrella; valet over open lots |
| 60°F+ clear | Mention patio option if restaurant has one |
| Wind > 15 mph | Skip patio recommendation regardless of temp |
| High AQI (inversion) | Briefly note if severe; shorter outdoor exposure |

### Weather Line in Brief

```
🌡️ **Weather:** ~{temp} and {conditions}. {advisory}
```

**Examples:**
> *🌡️ Weather: 29°F with light snow — bundle up. Covered parking is closest, about a half-block walk.*

> *🌡️ Weather: 67°F and clear — beautiful evening. The patio should be great if you want it.*

> *🌡️ Weather: 42°F, partly cloudy. A jacket will do.*

---

## 6. Pre-Evening Brief Template

Assemble all gathered intel into a clean summary:

```
📋 **Evening Brief: {Event/Restaurant}**
📅 {Day, Date} at {Time} — {details}

🍽️ **Menu highlights:** {signature dishes, price range}
      {any dietary-relevant note}
👔 **Dress code:** {recommendation}
🅿️  **Parking:** {valet/garage/street}
🚗 **Drive time:** ~{X} min from home — leave by {time}
🌡️ **Weather:** ~{temp}, {conditions}. {advisory}
📝 **Note:** {cancellation policy, occasion, anything else}
```

---

## 7. Dessert & After Spots

Suggest an optional extension — keep it light, don't make it feel mandatory.

### Find Options Near Venue / Restaurant

```bash
web_search "dessert near {venue or restaurant address} open late"
web_search "ice cream cafe {city} open after 9pm"

goplaces nearby "{address}" --type bakery --open-now --limit 5 2>/dev/null || true
goplaces search "dessert cafe {city}" --min-rating 4.0 --limit 5 2>/dev/null || true
```

### Check Closing Times First

Only suggest spots that close ≥ 60 minutes after the estimated end of the evening:

```bash
web_search "{spot name} {city} hours"
goplaces search "{name}" --limit 1 --json 2>/dev/null | \
  jq '.[0].currentOpeningHours.weekdayDescriptions' 2>/dev/null || true
```

### Dietary Preferences Apply Here Too

If `dietary` includes `"no alcohol"`: recommend dessert cafes, creameries, bakeries — not cocktail bars or wine bars. If `dietary` includes allergy notes, check menu before recommending.

### How to Present

Frame as optional — one concise sentence:

> "Want to keep the evening going? There's a {spot} about {distance} from the restaurant, open until {time} — great for dessert."

Only surface if:
- Evening ends before 9 PM (enough time)
- User seems to be planning a full evening
- User explicitly asks about after-dinner options

---

## 8. Movie / Event Pre-Show Intel

For evenings that include a movie or event:

```bash
# Find venue details
web_search "{venue name} bag policy entry requirements"
web_search "{venue name} parking options prepay"

# For events: check if there's an opener/support act
web_search "{artist/event} {venue} {date} opener doors time"
```

**Pre-show brief additions:**
```
🎤 **Doors:** {doors open time}
🎭 **Show starts:** {start time} (arrive by {doors time} for best experience)
🎒 **Bag policy:** {clear bag required / small clutch / no restrictions}
🅿️  **Parking:** {venue-specific options; pre-pay if available}
```


path: references/resy-flow.md
content: # Resy Booking Flow — Detailed Reference

Step-by-step browser automation for booking on Resy using `playwright-cli`.

Before starting, load config: `cat ~/.openclaw/skills/date-night/config.json`

All commands assume PATH is set: `export PATH="$HOME/.npm-global/bin:$PATH"`

---

## Key Differences from OpenTable

| Aspect | OpenTable | Resy |
|--------|-----------|------|
| Guest checkout | ✅ Yes | ❌ No — account required |
| Auth method | Phone verification per booking | Login (email+password or phone+SMS) |
| URL structure | `/r/{slug}` | `/cities/{city}/{slug}` |
| Availability widget | Modal popup | Inline on restaurant page |
| Deposit / prepayment | Rare | Common at premium restaurants |
| Modification | Direct | Cancel + rebook only |

## Important: Resy Requires Authentication

Resy **requires a user account** to complete a booking. Options:

1. **Has Resy account** → login and proceed
2. **No account** → create one, or suggest OpenTable instead
3. **Saved auth state** → load to skip login

```bash
playwright-cli state-load ~/.openclaw/skills/date-night/state/resy-auth.json 2>/dev/null
```

---

## 1. Restaurant Page URL

```
https://resy.com/cities/{city-slug}/{restaurant-slug}
```

City slug format: `{city}-{state-abbrev}` (lowercase, hyphenated). Examples:
- `salt-lake-city-ut`, `new-york-ny`, `los-angeles-ca`, `chicago-il`

Search: `https://resy.com/cities/{city-slug}/search?query={name}`

## 2. Open & Initial Snapshot

```bash
playwright-cli -s=resy open "https://resy.com/cities/{city-slug}/{restaurant-slug}" --headed
playwright-cli -s=resy snapshot
```

Page shows: restaurant info, inline reservation widget (date picker, party size, time slots).

## 3. Set Reservation Parameters

### Party Size
```bash
playwright-cli -s=resy click {party-size-ref}
playwright-cli -s=resy snapshot
playwright-cli -s=resy click {size-option-ref}
```

### Date Selection
```bash
playwright-cli -s=resy click {date-picker-ref}
playwright-cli -s=resy snapshot
playwright-cli -s=resy click {next-month-ref}  # if needed
playwright-cli -s=resy click {date-ref}
```

### Time / Seating Type
```bash
playwright-cli -s=resy snapshot
# Each button shows time + seating type
# Prefer: Dining Room, Patio, Chef's Table
# Avoid: Bar, Lounge (unless clearly food-focused)
# Apply dietary config: if no alcohol preference, avoid bar-adjacent seating
playwright-cli -s=resy click {time-slot-ref}
```

## 4. Authentication

Clicking a time slot triggers the auth check if not logged in.

### Email + Password Login
```bash
playwright-cli -s=resy fill {email-ref} "{config.user_email}"
playwright-cli -s=resy fill {password-ref} "{user_resy_password}"  # ask user
playwright-cli -s=resy click {login-button-ref}
```

### Phone + SMS Login
```bash
playwright-cli -s=resy fill {phone-ref} "{config.user_phone}"
playwright-cli -s=resy click {send-code-ref}
sleep 5
# Find Resy SMS code — see sms-codes.md for discovering Resy chat ID
playwright-cli -s=resy fill {code-ref} "{code}"
# Or use press if code field is in iframe:
playwright-cli -s=resy press {d}  # each digit
```

### Save Auth State After Login
```bash
mkdir -p ~/.openclaw/skills/date-night/state
playwright-cli -s=resy state-save ~/.openclaw/skills/date-night/state/resy-auth.json
```

## 5. Confirm Reservation

After authentication:
```bash
playwright-cli -s=resy snapshot
playwright-cli -s=resy fill {special-requests-ref} "{dietary note or seating preference}"
playwright-cli -s=resy click {confirm-button-ref}
```

⚠️ **Deposit / prepayment:** Some Resy restaurants require a deposit or full prepayment. If payment form appears — **STOP**. Show total to user, get explicit approval before entering any payment info.

## 6. Confirmation Page

```bash
playwright-cli -s=resy snapshot
```

Capture: restaurant, date, time, party, confirmation number, cancellation policy deadline.

**Always note the cancellation deadline** to the user:
> "This reservation has a 48-hour cancellation policy. You'll need to cancel by {datetime} to avoid a fee."

---

## Resy Quirks

- **SPA (Angular):** Pages take longer to load/stabilize. Snapshot may return sparse content initially — wait and retry.
- **Seating types:** Multiple options per time slot; always choose Dining Room over Bar/Lounge.
- **No modification:** To change details, cancel + rebook. Warn user about resale risk at popular restaurants.
- **Cancellation fees:** Many Resy restaurants charge $25–100+/person for no-shows. Always communicate this.

## Resy SMS Discovery

Resy's SMS sender is different from OpenTable's. On first use:
```bash
# After triggering Resy SMS, search recent messages broadly
imsg history --limit 10 2>/dev/null | grep -i "resy\|verification\|code" || true
# Note the chat ID once found, add to sms-codes.md
```


path: references/search-events.md
content: # Finding Events — Detailed Reference

How to discover concerts, sports, comedy, theater, and festivals near you.

Load config: `CONFIG=$(cat ~/.openclaw/skills/date-night/config.json)`

---

## Quick Discovery

### Web Search (Fastest)
```bash
web_search "concerts near {config.location} {month} {year}"
web_search "events near {config.zip} this weekend"
web_search "comedy shows near {config.location} 2026"
web_search "Broadway shows {config.location} 2026 season"
```

### SeatGeek Browse (Best UX for Discovery)
```bash
playwright-cli -s=sg open "https://seatgeek.com/{config.city}-tickets" --headed
playwright-cli -s=sg snapshot
# Filter by: category (Concerts, Sports, Theater, Comedy), date range, price
```

### Ticketmaster Browse
```bash
playwright-cli -s=tm open "https://www.ticketmaster.com/{config.city}-tickets" --headed
playwright-cli -s=tm snapshot
```

---

## By Category

### Concerts / Music
SeatGeek → Concerts category in your city. Or:
```bash
playwright-cli -s=sg open "https://seatgeek.com/concerts/{config.city}-tickets" --headed
web_search "{artist name} {config.location} {year} tour dates"
```

### Sports
```bash
# General
web_search "sports events near {config.location} this month"
# Specific team schedule
web_search "{team name} home games {month} {year} schedule"
playwright-cli -s=tm open "https://www.ticketmaster.com/{team-slug}/artist/{id}" --headed
```

Find your local teams: `web_search "professional sports teams near {config.location}"`

### Comedy
```bash
web_search "comedy shows {config.location} 2026"
web_search "comedy clubs near {config.zip}"
# Many local comedy clubs sell tickets directly on their website
playwright-cli -s=comedy open "{local-comedy-club-url}" --headed
```

### Broadway / Touring Shows
```bash
web_search "Broadway touring shows {config.location} 2026 season"
web_search "{show name} touring schedule {config.location}"
# Check your city's main performing arts center
```

### Festivals / Outdoor Events
```bash
web_search "festivals near {config.location} {season} {year}"
web_search "outdoor events {config.city} {month} {year}"
```

---

## Date-Range Search

When looking for something on a specific date or weekend:

```bash
# SeatGeek with date filter
playwright-cli -s=sg open "https://seatgeek.com/{config.city}-tickets" --headed
playwright-cli -s=sg snapshot
# Apply date filter via UI: click date picker, set range
playwright-cli -s=sg click {date-filter-ref}
playwright-cli -s=sg click {start-date-ref}
playwright-cli -s=sg click {end-date-ref}
playwright-cli -s=sg click {apply-ref}
playwright-cli -s=sg snapshot

# Web fallback
web_search "events {config.location} {specific date}"
```

---

## Cross-Platform Price Check

```bash
export PATH="$HOME/.npm-global/bin:$PATH"
EVENT="{event name}"
CITY="{config.location}"

playwright-cli -s=sg open "https://seatgeek.com/search?q=${EVENT}&location=${CITY}" --headed &
playwright-cli -s=tm open "https://www.ticketmaster.com/search?q=${EVENT}" --headed &
playwright-cli -s=sh open "https://www.stubhub.com/search?q=${EVENT}+${CITY}" --headed &
wait

playwright-cli -s=sg snapshot
playwright-cli -s=tm snapshot
playwright-cli -s=sh snapshot
```

See [event-tickets.md](event-tickets.md) for the full comparison presentation format.

---

## Local / Niche Ticketing

Some smaller venues sell tickets directly. Find them:

```bash
web_search "{venue name} {city} tickets buy"
playwright-cli -s=venue open "{venue-website}" --headed
playwright-cli -s=venue snapshot
```

For local venues, skip the major platforms and book direct — lower fees, often easier checkout.

---

## Event Discovery Output Format

```
🎵 **Events Near {config.location} — {Month Year}**

1. 🎤 **{Artist / Show}**
   📅 {Date}, {Time}
   📍 {Venue} (~{drive time} from home)
   💰 From ${lowest} all-in | SeatGeek Deal Score: {N}/100
   🔗 Cheapest on {platform}

2. 🏀 **{Team} vs. {Opponent}**
   📅 {Date}, {Time}
   📍 {Arena} (~{drive time} from home)
   💰 From ${price} on Ticketmaster
```

**Notes:**
- Always show drive time from `config.location`
- Show all-in price when available
- Flag high-demand events: *"This one is moving fast — prices may increase"*
- Apply dietary preferences: if `"no alcohol"`, present alcohol-centric events neutrally (brewfests, wine events) — let user decide

---

## Plan Around an Event (Full Date Night Pattern)

```
1. Find event → compare prices → confirm with user → purchase tickets
2. Note: venue address + show start time
3. Search restaurants within 0.5 mi of venue, open for dinner
4. Calculate dinner window: show start - 1.5h = dinner end time
5. Suggest 2-3 dinner options with OpenTable availability
6. Book dinner (approval required)
7. Weather check for that evening
8. Calculate timeline: leave home → dinner → venue
9. Add both events to calendar
10. Draft partner notification → send with approval
11. Childcare prompt if has_children
12. Budget estimate: dinner + tickets + babysitter
13. Optional: dessert spot near venue for after
```


path: references/search-movies.md
content: # Finding Movies — Detailed Reference

How to discover what's playing, find showtimes, and choose the right film.

Load config: `CONFIG=$(cat ~/.openclaw/skills/date-night/config.json)`

---

## Quick Discovery Methods

### 1. Web Search
```bash
web_search "movies playing near {config.zip} this weekend"
web_search "new movies in theaters {month} {year}"
web_search "best date night movies in theaters now"
web_search "{config.preferred_theater} showtimes tonight"
```

### 2. Fandango (Best Aggregator — All Chains)
```bash
playwright-cli -s=fd open "https://www.fandango.com/movies-in-theaters?location={config.zip}" --headed
playwright-cli -s=fd snapshot
```
Shows all movies across nearby theaters with inline showtimes. Best for discovering options without knowing which chain.

### 3. IMDB Showtimes
```bash
playwright-cli -s=imdb open "https://www.imdb.com/showtimes/?zip={config.zip}&date={YYYY-MM-DD}" --headed
playwright-cli -s=imdb snapshot
```

### 4. Preferred Theater Direct
```bash
# Use config.preferred_theater if set
playwright-cli -s=movies open "https://{preferred_theater_url}" --headed
playwright-cli -s=movies snapshot
```

---

## Choosing a Date Night Movie

### Questions to Consider
1. Genre preference? (comedy, drama, action, thriller, romance)
2. Runtime — long film + dinner = late night
3. Rating? Most couples prefer PG-13; R depends on content
4. Worth a premium format (IMAX, Dolby, PLF) for this film?

### Rating Guide

| Rating | Suitability | Notes |
|--------|-------------|-------|
| G / PG | ✅ Great | Often high quality |
| PG-13 | ✅ Great | Sweet spot for most date nights |
| R | ⚠️ Depends | Violence/language usually fine; check content reason |
| NC-17 | ❌ Skip | |

**Apply dietary config:** No specific filter for movies, but if `dietary` reflects strong values (e.g., lifestyle preferences), consider noting if a film has heavy drug/alcohol themes.

### Review Sources
```bash
web_search "{movie title} {year} Rotten Tomatoes score"
web_search "{movie title} Metacritic score audience"
```

**Quick heuristic:**
- RT > 75% + Audience > 80% = solid bet
- RT < 50% but Audience > 70% = often good entertainment despite critics
- Both < 50% = skip unless specifically requested

---

## Showtime Selection

### Timing for Date Night

| Scenario | Recommended Showtime |
|----------|---------------------|
| Dinner before movie | 7:30–8:30 PM (eat 5:30–6:30) |
| Movie only | 7:00–8:00 PM |
| Dinner after (rare) | 5:00–6:00 PM (eat after at 8:00+) |

**Leave time calculation:**
```
Leave = Showtime - Drive time from {config.location} - 15 min buffer
```

Find drive time: `web_search "drive time from {config.location} to {theater address}"`

### Format Recommendations

| Film Type | Best Format |
|-----------|-------------|
| Action blockbuster, sci-fi | IMAX / PLF (worth the premium) |
| Drama, comedy, romance | Standard |
| Animated | Standard (3D usually not worth it) |

---

## Now Playing vs. Coming Soon

```bash
# Currently in theaters
web_fetch "https://www.fandango.com/movies-in-theaters" --maxChars 5000 2>/dev/null || true

# Upcoming releases
web_search "upcoming movies {month} {year} release dates"
```

**Set a reminder for upcoming films:**
```json
{
  "name": "movie-reminder-{film}",
  "enabled": true,
  "schedule": {"kind": "once", "at": "{release-date}T09:00", "tz": "{user-tz}"},
  "payload": {
    "kind": "agentTurn",
    "message": "Notify the user that '{Film}' is now in theaters. Check {preferred_theater} for showtimes near {config.zip} and suggest a date night. USE THE MESSAGE TOOL to notify via {config.notify_channel}.",
    "deliver": false
  }
}
```

---

## Movie Recommendation Format

Present 2–4 options max:

```
🎬 **Movies Playing — {Date}**
📍 {Theater Name} (your preferred theater)

1. **{Title}** — {Genre}
   ⏱️ {Runtime} | {Rating} | {Format}
   🍅 RT: {%} Critics · {%} Audience
   🎯 Date night fit: {brief reason}
   📅 Showtimes: {time1}, {time2}, {time3}

2. **{Title}** ...
```

Lead with your top recommendation for a date night.


path: references/search-restaurants.md
content: # Restaurant Search — Detailed Reference

How to find and evaluate restaurants by name, cuisine, location, or occasion.

Load config: `CONFIG=$(cat ~/.openclaw/skills/date-night/config.json)`

**Dietary preferences:** Apply `config.dietary` to all searches and recommendations. If `"no alcohol"` is set, never highlight wine lists, cocktail programs, or bar features.

---

## Method A: Web Search

```bash
# General discovery
web_search "best Italian restaurants {config.location} 2026"
web_search "romantic dinner restaurants {config.location} date night"
web_search "fine dining {config.location} OpenTable available"

# Find OpenTable slug
web_search "{restaurant name} {city} OpenTable"
# → URL gives slug: opentable.com/r/{slug}

# Find platform
web_search "{restaurant name} {city} reservations"
# → Shows whether it's OpenTable, Resy, Yelp, or phone-only
```

---

## Method B: Google Places (goplaces CLI)

```bash
# Text search
goplaces search "Italian restaurants {config.location}" --type restaurant --limit 5

# Filter by rating
goplaces search "fine dining {config.location}" --type restaurant --min-rating 4.5 --limit 5

# Filter by price level (1=cheap, 2=moderate, 3=expensive, 4=very expensive)
goplaces search "dinner {config.location}" --type restaurant --price-level 3 --price-level 4 --limit 5

# Nearby a location
goplaces nearby "{config.location}" --type restaurant --open-now --limit 10

# Full details for a specific restaurant
goplaces search "{restaurant name} {city}" --limit 1 --json | jq '.[0]' 2>/dev/null || true
```

---

## Method C: OpenTable Search URL

```
https://www.opentable.com/s?term={query}&covers={N}&dateTime={YYYY-MM-DDTHH:MM}&metroId={metro_id}
```

Metro IDs: NYC=1, LA=4, Chicago=5, Dallas=9, SF=13, SLC=72, Denver=6, Seattle=15, Boston=2, Miami=29.

```bash
playwright-cli -s=search open "https://www.opentable.com/s?term=Italian&covers=2&dateTime=2026-03-07T18:30&metroId={metro}" --headed
playwright-cli -s=search snapshot
```

---

## Method D: Resy Browse

```bash
# Browse all Resy restaurants in a city
playwright-cli -s=resy open "https://resy.com/cities/{city-slug}" --headed
playwright-cli -s=resy snapshot

# Search
playwright-cli -s=resy open "https://resy.com/cities/{city-slug}/search?query={term}" --headed
```

---

## Building a Recommendation

### Questions to Consider

1. **Cuisine preference?** (Italian, steak, sushi, American, Mexican, etc.)
2. **Occasion?** (Casual date, anniversary, birthday, celebration)
3. **Budget?** (Casual, upscale casual, fine dining)
4. **Location?** (Downtown, near a theater/venue, near home)
5. **Date / time?** (Check availability before recommending)

### Review Aggregation

Pull from multiple sources for a confident recommendation:

| Source | Tool | Notes |
|--------|------|-------|
| OpenTable | playwright snapshot or web_fetch | Verified diners — most reliable |
| Google | `goplaces details` | High volume |
| Yelp | `web_search` | Good signal, can be noisy |

```bash
# Google rating
goplaces search "{restaurant name} {city}" --limit 1 --json 2>/dev/null | \
  jq -r '.[] | "\(.name): \(.rating)/5 (\(.userRatingCount) reviews)"' 2>/dev/null || true

# OpenTable rating (from page snapshot or web_fetch)
web_fetch "https://www.opentable.com/r/{slug}" --maxChars 2000 2>/dev/null | \
  grep -i "rating\|reviews" | head -3 || true

# Yelp (web search)
web_search "{restaurant name} {city} Yelp rating reviews"
```

**Consolidated display:**
```
⭐ {Restaurant}: 4.9 OpenTable (813) · 4.6 Google (1.2K) · 4.5 Yelp (213)
```

**Weighting:** Prioritize review count. A 4.7 with 500 reviews beats a 4.9 with 12. If Yelp is significantly lower, flag it: *"Yelp is more mixed at 3.8 — worth checking recent reviews."*

---

## Recommendation Format

Present 2–4 options max (decision fatigue above 4):

```
🍽️ **{Restaurant Name}** — {Cuisine}
📍 {Neighborhood}, {City}
⭐ {rating} OpenTable ({N} reviews) · {rating} Google ({N})
💰 {$-$$$$} (~${est}/person before tip)
🎯 Why it fits: {one-sentence reason}
📅 Available: {times on target date}
```

**Include:**
- Signature dishes, food highlights
- Ambiance / atmosphere
- Dress code
- Parking availability

**Apply dietary filter:** If `dietary` contains `"no alcohol"`, omit wine lists, cocktail highlights, and bar descriptions entirely. If vegetarian, note plant-based options.

---

## Near-Venue Search

When the user wants dinner close to an event venue:

```bash
# Get venue address first
web_search "{venue name} {city} address"

# Find nearby restaurants
goplaces nearby "{venue address}" --type restaurant --limit 10 2>/dev/null || true
web_search "restaurants near {venue name} {city} open for dinner"
```

Filter by: closing time (must be open 2–3 hrs before show), walking distance (ideally < 0.5 mi from venue), price range from config.

---

## Past Dining History (Anti-Repeat)

Before recommending, check history to avoid obvious repeats:

```bash
cat ~/.openclaw/skills/date-night/history.jsonl 2>/dev/null | \
  jq -r '.restaurant' 2>/dev/null | sort -u || true
```

Then:
- Exclude recently visited restaurants (last 60 days)
- Suggest revisiting high-rated restaurants from history
- Track cuisine gaps: "You haven't tried Thai yet"


path: references/smart-features.md
content: # Smart Features — Detailed Reference

Advanced patterns: parallel availability, waitlist monitoring, reminders, budget estimates, recurring date nights, and history dashboard.

Load config: `CONFIG=$(cat ~/.openclaw/skills/date-night/config.json)`

---

## 1. Multi-Restaurant Parallel Availability

When the user asks "find me a table Saturday night" without a specific restaurant:

```bash
export PATH="$HOME/.npm-global/bin:$PATH"
DATE="2026-03-07"; TIME="18:30"; COVERS=2

# Open multiple restaurants in parallel sessions
playwright-cli -s=opt1 open "https://www.opentable.com/r/{slug-1}?covers=${COVERS}&dateTime=${DATE}T${TIME}" --headed &
playwright-cli -s=opt2 open "https://www.opentable.com/r/{slug-2}?covers=${COVERS}&dateTime=${DATE}T${TIME}" --headed &
playwright-cli -s=opt3 open "https://www.opentable.com/r/{slug-3}?covers=${COVERS}&dateTime=${DATE}T${TIME}" --headed &
wait

playwright-cli -s=opt1 snapshot
playwright-cli -s=opt2 snapshot
playwright-cli -s=opt3 snapshot

playwright-cli -s=opt1 close
playwright-cli -s=opt2 close
playwright-cli -s=opt3 close
```

**Alternative:** OpenTable search shows multiple restaurants with availability inline:
```bash
# Replace {METRO_ID} with the metro ID for your city
playwright-cli -s=search open "https://www.opentable.com/s?covers=${COVERS}&dateTime=${DATE}T${TIME}&metroId={METRO_ID}&sort=availability" --headed
playwright-cli -s=search snapshot
```

Common metro IDs: New York=1, Los Angeles=4, Chicago=5, Dallas=9, San Francisco=13, Salt Lake City=72.

**Comparison output:**
```
📊 **Availability for {Date} (Party of {N}):**

1. 🍽️ **{Restaurant A}** — 6:00, 6:30, 8:15
2. 🍽️ **{Restaurant B}** — 5:30, 7:45, 8:30
3. 🍽️ **{Restaurant C}** — 6:15, 6:45, 7:00

Which catches your eye?
```

---

## 2. Waitlist Monitoring

When a desired reservation is unavailable:

```json
{
  "name": "reservation-monitor-{restaurant}-{date}",
  "enabled": true,
  "schedule": {"kind": "interval", "every": "30m"},
  "sessionTarget": "isolated",
  "payload": {
    "kind": "agentTurn",
    "message": "Check if a {time-range} table for {N} opened up at {Restaurant} on {date}. Use playwright-cli to check https://www.opentable.com/r/{slug}?covers={N}&dateTime={date}T{time} — snapshot and look for available time slots. If found, USE THE MESSAGE TOOL to notify the user via {config.notify_channel}. Include available times. If no availability, do nothing. Disable this job after 24 hours or after booking.",
    "deliver": false,
    "thinking": "high"
  }
}
```

**Monitoring cadence:**
| Event is... | Interval |
|-------------|----------|
| Weeks away | Every 2–4 hours |
| This week | Every 30–60 min |
| Tomorrow | Every 15 min |
| High-demand venue | Every 15–30 min |

Notification when found:
> "🍽️ Table available! {Restaurant} has a {time} opening on {date} for {N}. Want me to book it?"

---

## 3. Reservation Reminders (Day-Of)

After booking, create a morning-of scheduled reminder:

```json
{
  "name": "dinner-reminder-{restaurant}-{date}",
  "enabled": true,
  "schedule": {
    "kind": "cron",
    "expr": "0 9 {day} {month} *",
    "tz": "{config.timezone or America/Chicago}"
  },
  "sessionTarget": "isolated",
  "payload": {
    "kind": "agentTurn",
    "message": "Send a dinner reminder for tonight. Restaurant: {name}. Time: {time}. Party: {N}. Confirmation: {number}. Include: dress code, parking, drive time from {config.location}, weather forecast using wttr.in/{config.location}?format=3, and 'leave by' time. Ask if childcare is confirmed if {config.has_children}. USE THE MESSAGE TOOL to send via {config.notify_channel}.",
    "deliver": false
  }
}
```

---

## 4. Dining History Tracking

**File:** `~/.openclaw/skills/date-night/history.jsonl`

### Extended Schema
```json
{
  "date": "2026-03-07",
  "time": "19:00",
  "restaurant": "The Capital Grille",
  "platform": "opentable",
  "party_size": 2,
  "occasion": "date night",
  "confirmation": "OT-12345",
  "address": "40 S Main St, {City}",
  "cuisine": "Steakhouse",
  "price_range": "$$$$",
  "event": null,
  "event_venue": null,
  "event_type": null,
  "total_cost": 187,
  "weather": "28°F, clear",
  "rating": 4.5,
  "notes": "",
  "would_return": true,
  "with": ["{config.partner}"],
  "cancelled": false
}
```

**New fields:** `event`, `event_venue`, `event_type` (dinner paired with show/movie), `total_cost` (entire evening), `weather`, `would_return`.

### Write Entry
```bash
mkdir -p ~/.openclaw/skills/date-night
cat >> ~/.openclaw/skills/date-night/history.jsonl << 'ENTRY'
{"date":"{YYYY-MM-DD}","time":"{HH:MM}","restaurant":"{name}","platform":"{platform}","party_size":{N},"occasion":"{occasion}","confirmation":"{num}","address":"{addr}","cuisine":"{cuisine}","price_range":"{$$}","total_cost":null,"rating":null,"would_return":null,"weather":null,"notes":"","cancelled":false}
ENTRY
```

---

## 5. Budget Estimate

Calculate and present estimated total cost after planning, before committing.

### Dinner Estimate (from price symbol)

| Symbol | Est. per Person | For 2 with 20% Tip |
|--------|-----------------|--------------------|
| $ | ~$15–25 | ~$36–60 |
| $$ | ~$25–50 | ~$60–120 |
| $$$ | ~$50–80 | ~$120–192 |
| $$$$ | ~$80–120 | ~$192–288 |

### Babysitter Estimate (if has_children)

```bash
RATE=$(cat ~/.openclaw/skills/date-night/config.json 2>/dev/null | jq -r '.babysitter_rate // 18')
HOURS=4  # adjust: dinner only=2.5h, dinner+movie=4-5h, dinner+concert=5-6h
echo "Babysitter: ~\$$(( HOURS * (RATE - 2) ))–\$$(( HOURS * (RATE + 2) ))"
```

### Full Budget Presentation

```
💰 **Date Night Budget Estimate**

🍽️  Dinner at {Restaurant} (×{party_size}):   ~${dinner_low}–${dinner_high}
     ({price_symbol} pricing + ~20% tip)

🎬/🎤  {Movie/Event} tickets (×{N}):           ${ticket_total}

👶  Babysitter (~{hours} hrs @ $${rate}/hr):  ~${sitter_low}–${sitter_high}

──────────────────────────────────────
📊  **Estimated total: ~${grand_low}–${grand_high}**
```

Omit babysitter line if `has_children: false`. Make ticket line configurable — omit if dinner only.

### Log Actual Spend Post-Evening

```bash
python3 -c "
import json, os
path = os.path.expanduser('~/.openclaw/skills/date-night/history.jsonl')
lines = open(path).readlines() if os.path.exists(path) else []
out = []
for line in lines:
    r = json.loads(line.strip())
    if r.get('restaurant') == '{Restaurant}' and r.get('date') == '{YYYY-MM-DD}':
        r['total_cost'] = {actual}
        r['rating'] = {rating}
        r['would_return'] = {true|false}
        r['notes'] = '{notes}'
    out.append(json.dumps(r))
open(path,'w').write('\n'.join(out)+'\n')
print('Updated.')
" 2>/dev/null || true
```

---

## 6. Recurring Date Night Reminders

Set up a monthly nudge so date nights don't get buried in life.

### Cron Job

```json
{
  "name": "date-night-reminder",
  "enabled": true,
  "schedule": {
    "kind": "cron",
    "expr": "0 10 1-7 * 6",
    "tz": "{config.timezone or user's local TZ}",
    "comment": "First Saturday of each month at 10 AM"
  },
  "sessionTarget": "isolated",
  "payload": {
    "kind": "agentTurn",
    "message": "Check ~/.openclaw/skills/date-night/history.jsonl for the most recent date night entry. Then send a friendly date night nudge to the user via {config.notify_channel}. Include: when the last date night was, where they went, and 2-3 restaurant suggestions from web_search that they HAVEN'T been to recently (check history to exclude repeats). Keep the tone warm and casual.",
    "deliver": false
  }
}
```

### Schedule Variants

| Preference | Cron Expression |
|------------|----------------|
| First Saturday monthly | `0 10 1-7 * 6` |
| Every 2 weeks (Friday) | `0 10 */14 * 5` |
| Every 3 weeks | Use interval: `every: "21d"` |

### Anti-Repeat Logic

Check history before suggesting restaurants:

```bash
# Restaurants visited in the last 90 days
cat ~/.openclaw/skills/date-night/history.jsonl 2>/dev/null | \
  jq -r 'select(.date >= "'$(date -v-90d +%Y-%m-%d 2>/dev/null || date -d '90 days ago' +%Y-%m-%d)'") | .restaurant' \
  2>/dev/null | sort -u || true
```

Exclude these from suggestions.

---

## 7. Date Night History Dashboard

Query patterns for the history file.

**"Where have we been?"**
```bash
cat ~/.openclaw/skills/date-night/history.jsonl 2>/dev/null | \
  jq -r '[.date, .restaurant, .cuisine, (.rating | tostring)] | @tsv' 2>/dev/null | sort -r
```

**"What was our favorite?"**
```bash
cat ~/.openclaw/skills/date-night/history.jsonl 2>/dev/null | \
  jq -s '[.[] | select(.rating != null)] | sort_by(-.rating) | .[] |
  "\(.rating)/5 — \(.restaurant) (\(.date))"' 2>/dev/null
```

**"Somewhere we haven't been?"**
```bash
# Get visited restaurants to exclude
cat ~/.openclaw/skills/date-night/history.jsonl 2>/dev/null | \
  jq -r '.restaurant' 2>/dev/null | sort -u
# Then search for restaurants NOT in this list
```

**"How much do we usually spend?"**
```bash
cat ~/.openclaw/skills/date-night/history.jsonl 2>/dev/null | \
  jq -s '[.[] | select(.total_cost != null) | .total_cost] |
  if length > 0 then "Average: $\(add/length | round)" else "No cost data yet" end' \
  2>/dev/null
```

**"Stats this year"**
```bash
YEAR=$(date +%Y)
cat ~/.openclaw/skills/date-night/history.jsonl 2>/dev/null | \
  jq -s --arg y "$YEAR" '[.[] | select(.date | startswith($y))] | {
    count: length,
    avg_cost: ([.[] | .total_cost // 0] | if length > 0 then add/length else null end),
    favorite_cuisine: (group_by(.cuisine) | max_by(length) | .[0].cuisine // "N/A"),
    pct_return: (if length > 0 then ([.[] | select(.would_return == true)] | length) * 100 / length | round else null end)
  }' 2>/dev/null
```

**Dashboard presentation:**
```
📊 **Date Night Dashboard — {Year}**

🍽️  Total date nights: {count}
💰  Average spend: ~${avg}/night
⭐  Top rated: {restaurant} ({rating}/5)
🔁  Would return: {pct}%
🍝  Favorite cuisine: {cuisine}

**Recent:**
{date} — {restaurant} ⭐{rating} {✅ if would_return}
...
```


path: references/sms-codes.md
content: # SMS Verification Codes

How to handle SMS verification codes during booking flows.

Load config: `CONFIG=$(cat ~/.openclaw/skills/date-night/config.json)`

---

## Overview

OpenTable sends a 6-digit verification code via SMS when completing a guest checkout. The code arrives from a short code and must be entered during the booking flow.

---

## OpenTable Verification

**Short code:** 22395 (US)
**Code length:** 6 digits
**Expiry:** 10 minutes

### First-Time Setup

You need to discover your SMS client's chat ID for the OpenTable short code. On first use:

```bash
# If using imsg (iMessage CLI)
# After triggering an OpenTable verification, find the chat:
imsg chats --limit 20 --json 2>/dev/null | jq -r 'select(.name | test("22395|opentable";"i")) | "\(.id) \(.name)"' 2>/dev/null || true

# Or search recent messages for the verification sender only:
imsg history --limit 10 2>/dev/null | grep -i "opentable\|22395\|verification code" || true
```

Once you find it, **save the chat ID** to `~/.openclaw/skills/date-night/config.json`:
```bash
cat ~/.openclaw/skills/date-night/config.json | \
  jq '. + {"opentable_sms_chat_id": {CHAT_ID}}' > /tmp/conf.json && \
  mv /tmp/conf.json ~/.openclaw/skills/date-night/config.json
```

### Retrieve Code
```bash
OT_CHAT=$(cat ~/.openclaw/skills/date-night/config.json | jq -r '.opentable_sms_chat_id // "unknown"')

# Get latest code
imsg history --chat-id ${OT_CHAT} --limit 1 2>/dev/null | grep -oE '[0-9]{6}' || true

# If chat ID unknown, broad search:
imsg history --limit 10 2>/dev/null | grep -oE '[0-9]{6}' | head -1 || true
```

---

## Resy Verification

**Short code:** TBD — discover on first Resy booking.
**Code length:** 4–6 digits

```bash
# After triggering Resy SMS:
imsg history --limit 10 2>/dev/null | grep -i "resy\|verification\|code" || true
# Note the chat ID and update config similarly to OpenTable above
```

---

## Entering Codes in Cross-Origin Iframes

Both OpenTable and Resy may render verification inputs in **cross-origin iframes** where DOM access is blocked. Use keyboard press instead of fill:

```bash
# Code: 714289
playwright-cli press 7
playwright-cli press 1
playwright-cli press 4
playwright-cli press 2
playwright-cli press 8
playwright-cli press 9
```

**Tips:**
- Verification input usually auto-focuses — check snapshot first
- If not focused: `playwright-cli press Tab` to move focus
- Some forms auto-submit after all digits are entered
- Wrong digit: `playwright-cli press Backspace` to correct

---

## Resend Code

If code doesn't arrive within ~15 seconds:

```bash
playwright-cli snapshot
playwright-cli click {resend-code-ref}
sleep 10
imsg history --chat-id ${OT_CHAT} --limit 1 2>/dev/null | grep -oE '[0-9]{6}' || true
```

---

## Troubleshooting

| Issue | Solution |
|-------|----------|
| No code received | Wait up to 60s. Click "Resend code". Check cellular signal. |
| Code expired | Click "Resend code" for a fresh code. Move quickly on retry. |
| Multiple codes in history | Use the most recent (highest timestamp). |
| Code from wrong service | Verify chat ID — filter by short code number if possible. |
| SMS client not returning results | Test with `imsg chats --limit 5` to verify CLI is working. |
| Wrong SMS tool configured | Check `config.notify_channel` — different tool may be needed |

---

## Alternative SMS Tools

If not using `imsg` (iMessage), adapt the code retrieval for your SMS client:

| Tool | Command Pattern |
|------|----------------|
| iMessage (imsg) | `imsg history --chat-id {id} --limit 1` |
| Telegram | Check Telegram bot or specific chat for SMS forwarding |
| Other | Adapt based on your SMS CLI / tool |

The underlying pattern is always: wait for SMS → find in inbox → extract digits → press each digit individually in the browser.


Security scan verdicts — ClawScan: suspicious | static analysis:  | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)