Skip to content
Back to skills

Dine

ASecurity

Restaurant recommendation flow using local context and credit-burn signals.

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 9, 2026
ai-agentspythonrustgitapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 9, 2026

npx -y skills add mhxie/Atelier --skill dine --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dine?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dine
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mhxie-dine-atelier/badge)](https://www.skillsdirectory.com/skills/mhxie-dine-atelier)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dine
description: Restaurant recommendation flow using local context and credit-burn signals.
---
## Purpose

Four intents (auto-detected from args):
- **A. Restaurant Recommendation** (default): pick 3 restaurant candidates based on user-supplied context, historical preferences, and credit-burn opportunities. Read-only on catalog docs under this intent.
- **B. Workplace Catering Tracker**: parse a weekly catering PDF from the folder mapped to a workplace slug in `profile/diet.md`, choose health-aware picks for the user's attendance days, and surface a confirmed table for the user to record themselves (the system does not write to daily notes).
- **C. Meal Log Capture** (ad-hoc): log a meal the user just ate. Parses receipt images (HEIC/JPG/PNG/PDF) when provided, cross-references catalogs for missing slots, asks ONE compact question for what cannot be derived, shows a draft row + side-effect plan, and appends to the private meal-history tracker on confirm. An explicitly trip-associated capture may also add a date-only meal-history reference to that trip note. Co-equal capture path with `/hi` Dining Pulse.
- **D. Establishment Update**: update one physical branch's address or lifecycle without creating a meal-history row.

## Quick start

Intent A examples:
- `/dine` → ask all context
- `/dine 工作日午餐` → use as scene hint, ask remaining
- `/dine 朋友 4 人 川菜 dinner` → use as filters, ask remaining
- `/dine <city> burn credit` → location hint + flag credit-burn priority

Intent B examples (first arg = workplace slug mapped in gitignored `profile/diet.md`):
- `/dine <slug>` → find the latest PDF in the mapped catering folder covering this week, pick per `profile/diet.md` attendance pattern
- `/dine <slug> <pdf-path>` → explicit PDF
- `/dine <slug> all` → all 5 weekdays (override)
- `/dine <slug> <day-codes>` → custom attendance set (override; any day-code combination works)

Intent C examples (logging a meal you just ate):
- `/dine log 今天午饭吃的 <restaurant> 两人 $<amount>` → free-text meal report
- `/dine 今天晚饭吃的是 /path/to/receipt.heic` → receipt image (HEIC auto-converted before Read)
- `/dine 昨天 <restaurant> dinner $<amount>` → dated free text (respects late-sleep rule)
- `/dine log /path/to/receipt.pdf` → receipt PDF outside any catering folder

Intent D examples (maintaining a physical branch):
- `/dine status <restaurant> <branch> closed`
- `/dine status <restaurant> <branch> moved <new-address>`

If args present, parse them as initial filters; only ask for slots not derivable.

## Step 0: Intent detection

Parse args. Precedence: **D → B → C → A** (most specific match wins; ambiguous → ask user one line before routing).

Read `profile/diet.md` once, if present, to resolve workplace slugs, catering folders, and `## Catalog files`. Do not assume the private mappings from examples in this command.

Route to **Intent D** when the leading subcommand is `status`.

Else route to **Intent B** if any of:
- First arg matches a workplace `Slug` declared in `profile/diet.md`
- Any arg is a `.pdf` path under a catering `Folder` declared in `profile/diet.md`
- Args contain the literal token `catering`

Else route to **Intent C** if any of:
- Leading subcommand `log` (e.g., `/dine log <freetext>`)
- Args contain past-tense / reporting markers: `记录` / `log` / `吃了` / `吃的` / `刚吃完` / "今天X吃的" / "昨天Y" / "just had"
- An image path (`.heic` / `.jpg` / `.jpeg` / `.png`) is provided
- A `.pdf` path is provided **outside** any `$OV/*/catering/` folder
- Free text mentions a specific restaurant + amount/party (e.g., `<name> 两人 $<amount>`) without a forward-looking verb

Do NOT route to C if the message is forward-looking: contains `推荐` / `去哪吃` / `想吃` / `what should I eat` / `where to eat` / `今晚吃什么` → fall through to A.

Otherwise route to **Intent A** (continue to Step 1 below).

For Intent B, jump to the "Intent B: Workplace Catering Tracker" section near the bottom and skip Steps 1-5.
For Intent C, read `protocols/dining-capture.md` and follow it; skip Steps 1-5.
For Intent D, jump to the "Intent D: Establishment Update" section near the bottom and skip Steps 1-5.

## Step 1: Gather context

For missing slots, ask via `AskUserQuestion` or sequential 1-line prompts (whichever fits faster). Required slots first; optional slots only if useful.

| Slot | Options | Required |
|---|---|---|
| **Location** | Home region / nearby city / travel destination / other | Y |
| **Party** | Solo / Partner / Family (N) / Friends (N) / Mixed work | Y |
| **Meal** | Lunch / Dinner / Brunch / Late night | Y |
| **Time budget** | Quick (<30min) / Standard (1-2h) / Leisurely (2h+) | Y |
| Mood / cuisine | Free text or options declared in `profile/diet.md` | N (default any) |
| **Health filter** | options enumerated in `profile/diet.md` ("Health filter input options" section) plus `no preference` | N (default no preference) |
| Budget cap | User-supplied cap / no cap | N |
| Avoid recent | Last 30 / 60 / 90 days | N (default 30) |

## Step 2: Load data (parallel)

Resolve the following roles through `profile/diet.md § Catalog files`. Paths are relative to `$OV` unless absolute. If the profile or a mapping is absent, use structural discovery under `<paths.travel>/` and `<paths.finance>/` as a fallback and disclose the gap.

- Regional dining catalog (rotation + Michelin wishlist + 场景索引 + `门店索引`), under `<paths.travel>/`
- Meal-history tracker (history with 评分 + 再去 + recency), under `<paths.travel>/`
- Credit-perks dining catalog (eligibility + city catalogs), under `<paths.travel>/`
- Benefits tracker (current cycle credit status, for burn signal), under `<paths.finance>/`
- Prepaid-balance tracker (balances per restaurant, for a soft "use it" signal), under `<paths.finance>/`
- For travel destinations: use the matching city section plus the corresponding local guide under `<paths.archive>/practical/travel/`

**Missing-file fallback:** if any of these is absent, skip it silently and note the gap in the closing line ("scored without [missing source]"). The recommendation still produces; the user can decide whether to recreate the catalog.

**Log-side aggregates (deterministic):** run
`uv run scripts/dine_rank.py --avoid-days <window>` first; it returns
per-restaurant visit counts, last-visit recency, `评分` averages, `再去`
state, the log-derived score component, and the avoid-window exclusions.
Do not re-read the meal-history tracker row by row; combine the returned
`log_score` with the catalog-side factors below.

**Integrity preflight:** when running from the Atelier repo, execute `python3 scripts/dining_audit.py --json`. Use its `establishments` rows as the address/lifecycle source. If it reports errors, exclude the affected rows or source from scoring and disclose the degraded input. Warnings such as missing legacy values do not block recommendations.

## Step 3: Filter + score

**Hard filters** (eliminate non-matches):
- Location matches user's region
- Cuisine NOT in avoid list
- Estimated price ≤ budget cap (allow 20% margin)
- Drive time fits time budget (heuristic: 🚗 count × 15min one-way)
- For Quick lunch: ⌛ ≤ 1
- For "Special occasion": Michelin OR Exclusive Tables only
- Skip restaurants visited within `avoid recent` window (from the meal-history tracker)
- Skip physical branches whose lifecycle is `closed` or `moved`; allow `unknown` only with an explicit lifecycle warning

Treat `(餐厅, 分店)` as establishment identity. Join visit ratings only to an exact branch-specific restaurant name; never assign a chain-wide or legacy ambiguous score to a branch by inference.

**Soft ranking** (order the survivors). `dine_rank.py` owns the log-side
score (`log_score`, itemized in `log_score_parts`: 评分 averages, 再去, rusty
recency); take it verbatim. Then apply the catalog-side priorities, strongest
first:

1. **Credit-burn priority:** an Exclusive Tables restaurant whose relevant cycle
   has unused credit and ≤ 60 days to deadline outranks everything else.
2. Michelin star match when the mood is "Special occasion".
3. Catalog 评 (higher first) and a 场景索引 match in the regional catalog.
4. Novelty: never visited when the mood is "Surprise" or "探索"; old-favorite
   revisit when rotation 评 ≥ 2 and `days_since` > 60 from dine_rank.
5. **Health filter active:** apply the "Health-filter scoring rules" section of
   `profile/diet.md` (recent-visit penalties, clean-style bonuses,
   cumulative-load adjustments) as exclusions and tie-breakers.

Exact weights, if wanted, belong in `dine_rank.py` beside `log_score`.

## Step 4: Output

Top 3 candidates as a table:

```markdown
| # | 餐厅 | 类型 | $ | 距离·等待 | Why | Credit signal |
|---|---|---|---|---|---|---|
| 1 | <restaurant-A> | <cuisine> | <$range> | <distance·wait> | <reason from catalog/log: 评 N + scene fit + recency> | n/a |
| 2 | <restaurant-B> | <cuisine> ⭐ | <$range> | <distance·wait> | <reason: Michelin tier + last log rating + want-revisit>; **<credit-card> <perk-program> <half> deadline <MM/DD> ($<amount>)** | 🔥 burn |
| 3 | <restaurant-C> | <cuisine> ⭐ | <$range> | <distance·wait> | <reason: novelty + Michelin tier + perk-eligible>; <perk-program> 候选 | <credit-card> <half> ✓ available |
```

Brief reasoning paragraph (2-3 lines) below the table:
- Mention the top filter constraints applied
- Flag any credit-burn 紧迫性 in plain text
- If filter returned <3 candidates, note relaxation taken (e.g., "loosened distance to 🚗🚗")

## Step 5: Close

End with one line:
> "选哪个? (回 1/2/3) 我帮你在常用 booking platforms 查时段, 或者 /dine + 新约束 重排"

Do NOT auto-book; just surface candidates.

## Intent B: Workplace Catering Tracker

### B.1 Resolve PDF

- If an arg is a `.pdf` path → use it directly.
- Else: resolve the slug's `Folder` from `profile/diet.md`, list `<Folder>/*.pdf`, and pick the one whose filename date range covers the current calendar week. Typical filename pattern: `<Workplace> Catering_<Mon> <DD>-<Mon> <DD>.pdf`. If multiple match (e.g., manual override), prefer the most recent `mtime`.
- Optional date arg `YYYY-MM-DD` shifts the target week (Mon of that week).
- 0 matches: report `本周菜单还没传到 <configured folder>` and exit cleanly.

### B.2 Parse menu

Read the PDF (`Read` tool). Extract per-day sections (Mon/Tue/Wed/Thu/Fri). Each day has a theme + items + dietary tags (`v` / `vg` / `mwgci`).

### B.3 Determine attendance days

Read attendance pattern from `profile/diet.md` (the section matching the resolved `<slug>`, key: `Attendance days`). Override via the second CLI arg:
- `all` → all 5 weekdays present in the PDF
- `<day-codes>` → custom set (case-insensitive day codes; any combination)

If `profile/diet.md` is absent or has no entry for `<slug>` → ask the user once, do not assume a default. Map each chosen day code to an absolute date based on the resolved week.

### B.4 Pick per day (reuse Step 3 health-filter logic)

Read **dietary picking priorities** and **flag taxonomy** from `profile/diet.md` (the `<slug>` section). Apply the policy verbatim — do not bake personal preferences into this committed file.

Generic fallback when `profile/diet.md` is absent: choose ONE protein + 1-2 veg sides per day, no specific oil/protein bias, and ask the user to confirm the picks before presenting.

The skill itself enforces only the structural shape (one row per attendance day, columns: protein + veg + sauce-note + flag). The semantic content is policy from the private file.

### B.5 Preview

Show table (one row per attendance day; values fill from B.4):

```markdown
| Date | Day | Theme | Pick | Flag |
|---|---|---|---|---|
| YYYY-MM-DD | <day> | <menu theme> | <protein> + <veg sides> + <sauce/dressing note> | <flag from profile/diet.md taxonomy> |
```

Add a 1-2 line cross-day note if `profile/diet.md` defines cross-day rules (e.g., protein rotation, 油脂 balance). Otherwise omit.

### B.6 Present

Show the user the per-day picks as ready-to-paste lines so they can record them in their daily notes themselves. The system does not write to daily notes.

For each attendance day, output one line in the format:
`<Slug> <YYYY-MM-DD> — <Day> <theme> (<pick>, <flag>)` where `<Slug>` is the user-provided slug capitalized (first letter only).

### B.7 Report

```
/dine <slug> summary (<week-range>)
  picks:  N   (date list)
```

## Intent C: Meal Log Capture

`protocols/dining-capture.md` owns this path: source resolution, catalog
cross-checks, explicit trip context, auto-derivation, the side-effect plan,
the confirmation gate, and the write and report steps. Read it and follow it.
It is shared with `/hi` Dining Pulse, which is why it is not inline here.

## Intent D: Establishment Update

Use this path for address or lifecycle maintenance without inventing a visit.

1. Resolve the regional catalog from `profile/diet.md`, then find an exact `(餐厅, 分店)` row in `门店索引`.
2. Accept only lifecycle values `active`, `closed`, `moved`, or `unknown`. Require a complete street address for a new row. Preserve an existing address unless the user explicitly changes it.
3. Show the current row and proposed row, then ask one confirmation: `Apply establishment update? (yes / no / edit)`.
4. On `yes`, update or append exactly one registry row, set `核验日` to the local effective date, record the source without copying private prose, and bump the catalog's `Last updated:` line.
5. Run `python3 scripts/dining_audit.py --json`. Repair only the introduced row if validation fails; otherwise report one line: `Updated: <restaurant> (<branch>) → <status>.`

Do not add a meal-history row, change ratings, or infer that another branch shares this lifecycle.

## Rules

Intent A:
- **Read-only on catalog docs (under Intent A)**: do NOT modify the regional dining catalog or the credit-perks catalog when handling a recommendation request. The meal-history tracker is also read-only under Intent A; appends route through Intent C (or `/hi` Dining Pulse).
- **0 candidates after hard filter**: relax most-restrictive constraint by 1 step, retry; surface 1-2 closest matches with flag "relaxed: <constraint>"
- **Always show credit-burn opportunity** if relevant under the live private benefits tracker. Even if the eligible restaurant does not match the exact mood, surface a fourth line using only the currently relevant benefit details.
- **Match user language**: Chinese-dominant if cuisine is Chinese; English if Western
- **Shape**: the candidate table, a short reasoning paragraph, one close line. Nothing else.
- **No web search**: cuisine + restaurant data comes from local catalog files only

Intent B:
- **Read-only on the PDF**: never modify the catering PDF
- **Read-only on daily notes**: daily notes are user-authored; the system surfaces picks for the user to record themselves and never writes to `<paths.daily_notes>/`
- **Does not touch the meal-history tracker**: workplace catering is excluded by design (low signal density per memory)
- **Per-day skip on parse failure**: if any one day's section fails to parse, skip that day with a logged warning; do not abort the whole batch
- **No web search**: menu data comes from the PDF only

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.