Use when processing a grocery store flyer for the Meal OS system. Triggered by "/meal-flyer" or "parse a flyer" or "process flyer".
Parse a weekly grocery store flyer and extract sale items mapped to canonical ingredient IDs.
Announce at start: "Processing store flyer..."
The user provides flyer data in one of two ways:
If the user provides a file path ending in .pdf:
If the user pastes text from a store website or flyer:
If no input is provided, ask: "Please provide the store flyer — either paste the text or give me the path to a PDF file."
Read data/config.yaml to get the user's store list.
Determine which store this flyer is from:
Look for a validity date range in the flyer content (e.g., "Valid 3/22 - 3/28", "Weekly Specials March 22-28").
Read data/canonical-ingredients.yaml from the project root. This is the normalization reference for mapping sale items.
If the file does not exist: Stop and tell the user: "data/canonical-ingredients.yaml is missing. Run /meal-setup first to initialize the project, then re-run /meal-flyer."
For each sale item in the flyer, extract:
| Field | What to capture | Examples |
|---|---|---|
| Item description | As printed in the flyer | "Boneless Skinless Chicken Thighs Family Pack" |
| Price | The sale price | $2.49, $8.99 |
| Unit | Price unit | /lb, /each, /pint, /dozen, /bag |
| Deal type | Type of promotion | Regular Sale, BOGO, Manager's Special, 2 for $5 |
For each food item:
Match against canonical-ingredients.yaml — find the closest canonical ID:
chicken_thighsalmoncherry_tomatogreek_yogurtUnmappable food items — if a food item doesn't match any canonical ingredient:
? in the canonical ID columnNon-food items — already excluded in Step 5; do not include any row for them in the table
Identify which ingredients from the core set (~24 items in canonical-ingredients.yaml) are on sale this week. List them with price and store name.
If no core ingredients are on sale, note: "No core ingredients on sale this week."
Identify items priced aggressively low — likely below store cost to drive foot traffic.
For each sale item with a "Save $X" value: compute savings_pct = save_amount / (sale_price + save_amount). Flag as loss leader if savings_pct >= 0.35.
Read data/receipts/price-history.yaml if it exists.
For each sale item that has a canonical ID (not ?):
If the price history file doesn't exist, skip Method 2 silently.
If any items are flagged, add a new section to the flyer markdown between "Core Ingredients On Sale This Week" and "Items Needing Review":
## Likely Loss Leaders
| Item | Sale Price | Regular/Avg Price | Savings | Method |
|------|-----------|-------------------|---------|--------|
| [Item] | $X.XX/unit | $Y.YY (regular) | NN% | heuristic |
| [Item] | $X.XX/unit | $Y.YY (avg from N receipts) | NN% | price-history |
| [Item] | $X.XX/unit | $Y.YY (regular) / $Z.ZZ (avg) | NN% | both |
Consider building meals around these items — they're priced to get you in the store.
If an item is flagged by both methods, show it once with Method = "both" and show both the regular and average prices.
If no items meet either threshold, omit this section entirely.
Generate filename: data/flyers/YYYY-MM-DD-<store-slug>.md
market-basket or hannafordsdata/flyers/2026-03-22-market-basket.mdIf the file already exists, ask: "A flyer for [store] on [date] already exists. Overwrite, save as [date]-[store]-2.md, or cancel?"
Ensure the directory exists: run mkdir -p data/flyers via Bash before writing.
Write to data/flyers/YYYY-MM-DD-<store-slug>.md using this format:
---