agentsclimarketplace

Meal flyer

Skill scurry/meals-agent/.claude/skills/meal-flyer

Use when processing a grocery store flyer for the Meal OS system. Triggered by "/meal-flyer" or "parse a flyer" or "process flyer".From its SKILL.md

Install
npx -y skills add scurry/meals-agent --skill meal-flyer

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

7.6 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it

/meal-flyer — Store Flyer Parser

Parse a weekly grocery store flyer and extract sale items mapped to canonical ingredient IDs.

Announce at start: "Processing store flyer..."

Step 1: Get Flyer Input

The user provides flyer data in one of two ways:

Option A: PDF File

If the user provides a file path ending in .pdf:

  • Read the PDF using the Read tool
  • For PDFs over 20 pages, paginate: read pages 1-20, then 21-40, etc.
  • Focus on food items — skip pages that are clearly non-food (cleaning supplies, pharmacy, seasonal items)
  • After the flyer markdown is successfully written and verified, delete the source PDF so raw flyer files do not accumulate in the repo

Option B: Pasted Text

If the user pastes text from a store website or flyer:

  • Use it directly

If no input is provided, ask: "Please provide the store flyer — either paste the text or give me the path to a PDF file."

Step 2: Load Config and Identify the Store

Read data/config.yaml to get the user's store list.

Determine which store this flyer is from:

  • Look for store name in the flyer content
  • If not obvious, ask: "Which store is this flyer from?" and list the stores from config as options

Step 3: Determine Date Range

Look for a validity date range in the flyer content (e.g., "Valid 3/22 - 3/28", "Weekly Specials March 22-28").

  • If found, use the start date for the filename
  • If not found, use today's date
  • Record the full date range for frontmatter

Step 4: Load Canonical Ingredients

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."

Step 5: Extract Sale Items

For each sale item in the flyer, extract:

FieldWhat to captureExamples
Item descriptionAs printed in the flyer"Boneless Skinless Chicken Thighs Family Pack"
PriceThe sale price$2.49, $8.99
UnitPrice unit/lb, /each, /pint, /dozen, /bag
Deal typeType of promotionRegular Sale, BOGO, Manager's Special, 2 for $5

Price normalization rules:

  • "2 for $5" → price: $2.50, unit: /each, deal type: "2 for $5"
  • "Buy One Get One Free" / "BOGO" → price: [listed price], deal type: "BOGO (effective 50% off)"
  • "Save $2" → note the savings but use the final sale price if shown
  • "$X.XX/lb" → price: $X.XX, unit: /lb
  • If price per unit can't be determined, record as-is with the deal description

Filtering rules:

  • Include: All food and beverage items
  • Skip entirely: Non-food items (cleaning supplies, paper goods, pet food, pharmacy, health/beauty). Do not include them in the Sale Items table. Track the count for the confirmation summary only.

Step 6: Map to Canonical Ingredients

For each food item:

  1. Match against canonical-ingredients.yaml — find the closest canonical ID:

    • "Boneless Skinless Chicken Thighs Family Pack" → chicken_thigh
    • "Fresh Atlantic Salmon Fillets" → salmon
    • "Grape Tomatoes Pint" → cherry_tomato
    • "Greek Yogurt Assorted" → greek_yogurt
  2. Unmappable food items — if a food item doesn't match any canonical ingredient:

    • Mark with ? in the canonical ID column
    • These go into an "Items Needing Review" section for the user to decide if they should be added to the canonical list
  3. Non-food items — already excluded in Step 5; do not include any row for them in the table

Step 7: Generate Summary

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."

Step 8: Detect Likely Loss Leaders

Identify items priced aggressively low — likely below store cost to drive foot traffic.

Method 1 — Savings Heuristic (always available)

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.

Method 2 — Price History Comparison (when data exists)

Read data/receipts/price-history.yaml if it exists.

For each sale item that has a canonical ID (not ?):

  1. Look up the canonical ID in the price history
  2. Compute the average price from all ledger entries for that item
  3. If the sale price is >30% below the average, flag as a likely loss leader

If the price history file doesn't exist, skip Method 2 silently.

Output

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.

Step 9: Check for Collisions

Generate filename: data/flyers/YYYY-MM-DD-<store-slug>.md

  • Store slug: market-basket or hannafords
  • Example: data/flyers/2026-03-22-market-basket.md

If the file already exists, ask: "A flyer for [store] on [date] already exists. Overwrite, save as [date]-[store]-2.md, or cancel?"

Step 10: Write the Flyer File

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:

---
store: [Store Name]
date_range: [YYYY-MM-DD to YYYY-MM-DD]
source: [weekly-flyer-pdf or pasted-text]
---

## Sale Items

| Item | Canonical ID | Price | Unit | Deal Type |
|------|-------------|-------|------|-----------|
| [Item description] | [canonical_id or ?] | [$X.XX] | [/unit] | [deal type] |

## Core Ingredients On Sale This Week

- [canonical_id] — $[price]/[unit] ([Store Name])
- [canonical_id] — $[price]/[unit] ([Store Name])

<!-- Include this section only if Step 8 flagged any loss leaders; omit entirely if none -->
## Likely Loss Leaders

| Item | Sale Price | Regular/Avg Price | Savings | Method |
|------|-----------|-------------------|---------|--------|
| [Item] | $X.XX/unit | $Y.YY (regular) | NN% | heuristic |

Consider building meals around these items — they're priced to get you in the store.

## Items Needing Review

- [Item description] → marked `?` ([reason or suggestion for canonical mapping])

If the source input was a local PDF file and the markdown flyer was written successfully, delete the PDF after saving the markdown output. Do not keep a separate archived PDF copy.

Step 11: Confirm

Output a summary:

Flyer saved: data/flyers/YYYY-MM-DD-<store-slug>.md

  Store: [Store Name]
  Date range: [date range]
  Items extracted: [N total]
  Mapped to canonical: [M items]
  Marked for review (?): [K items]
  Skipped (non-food): [J items]

  Core ingredients on sale:
  - [list or "none"]

  Loss leaders detected: [N items, or "none"]
  - [list if any]

  Source PDF deleted: [yes/no/not-applicable]

Review items marked with ? and decide if they should be added to data/canonical-ingredients.yaml.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.