| name | shopping-list-sync |
| description | Generate the final active shopping list by filtering out plain water and non-low staples from weekly ingredients, cleaning names, aggregating quantities, and merging with the active Mealie shopping list. |
Shopping List Sync Skill
This skill takes the raw ingredient strings from dinner recipes, a list of household staples, manually identified low staples, and the current active shopping list, and generates the final structured shopping list ready for database import.
Inputs
payload: A JSON object containing:
ingredients: A list of raw recipe ingredient strings (e.g. ["2 cloves garlic, minced", "1 lb chicken breast"]).
staples: A list of staple names already in the house (e.g. ["salt", "pepper"]).
inventory_items: A list of specific items the user wants to "use up" from their freezer/pantry/fridge (e.g. ["1 lb chicken thighs", "pesto sauce"]).
low_staples: A list of staple names that are currently running low and MUST be added (e.g. ["garlic"]).
manual_items: A list of manually added item names that must be preserved and categorized (e.g. ["toilet paper", "toothpaste"]).
available_labels: A list of actual category labels from the user's Mealie instance (e.g. ["1. Produce: Vegetables & Greens", "2. Bakery & Bread"]).
active_shopping_list: A list of objects representing the current active shopping list:
index: Integer, the array index of the item.
note: String, the item note/description.
checked: Boolean, the active checked state in Mealie.
family_dietary_rules: The family-specific dietary rules and preferences (which includes the "Dirty Dozen" list).
Workflow
-
Exclude Plain Water:
- Detect any ingredients representing plain tap water (e.g., "water", "cold water", "hot water", "tap water", "water to cover"). Exclude them entirely.
- Keep specialty waters that must be purchased (e.g., "coconut water", "rose water", "sparkling water").
-
Filter Staples and Inventory (Rigorous Semantic Matching):
- Compare every recipe ingredient against
staples and inventory_items.
- Deep Semantic Filtering: Do not just look for exact name matches. Use culinary knowledge to identify if an ingredient is a form of a staple.
- Example: If "Olive Oil" is a staple, filter out "Extra Virgin Olive Oil", "2 tbsp Olive Oil", "Olive oil for frying", etc.
- Example: If "Garlic" is a staple, filter out "3 cloves Garlic", "Minced Garlic", etc.
- Reverse Variant Matching: If the recipe calls for a generic ingredient (e.g., "Olive Oil" or "Vinegar") and a specific variety is listed in the
staples list (e.g., "Extra virgin olive oil" or "Red wine vinegar"), treat it as a match and filter it out.
- Exception Rule: If the matched staple is explicitly listed in
low_staples or is already on active_shopping_list, you MUST include it.
- Inventory Rule: If an ingredient matches an
inventory_item, filter it out.
- Rule of Thumb: If Nathan and Kristin already have it (Staple) or want to use it up (Inventory), and it's NOT low (Low Staples), do not put it on the shopping list.
-
Clean Ingredient Names & Organic Tagging:
- For each ingredient, extract the core name by removing quantities, units, and preparation instructions.
- Organic Tagging (Dirty Dozen): If the cleaned ingredient name matches any item from the "Dirty Dozen" list found in the
family_dietary_rules, automatically append (Buy Organic) to the name.
- Format and capitalize the resulting ingredient name in Title Case (e.g. "1 lb spinach" -> "Spinach (Buy Organic)", "3 cloves garlic" -> "Garlic").
-
Extract Unit and Aggregate Quantities:
- For each ingredient, extract the unit of measure (e.g., "lb", "oz", "cup", "can", "clove", "tsp", "tbsp").
- If the ingredient matches a staple, the should be .
Output
Return a JSON array of objects, where each object has these exact fields:
active_item_index: The matched active item's index integer, or null if it's a new item.
name: Cleaned, Title Cased name (e.g. "Chicken Breast").
quantity: Aggregated numeric quantity as a float.
unit: The extracted unit of measure (e.g. "lb", "cup", "can"), or null for staples.
checked: The matched checked state (boolean).
category: The EXACT zone name from available_labels.
- Do not include any other text or conversational response.