When the user asks you to buy groceries or household staples, do, repeat, or schedule a weekly grocery order, save or change their usual grocery list, fill a grocery cart they will check out themselves, turn a recipe or meal plan into a shopping list, or order from Instacart, Costco Same-Day, or another grocery delivery site, before you open the store, add anything to a cart, save a usual list to memory, or create a routine that reorders one.
Installs into .claude/skills of the current project.
Are you the author of Grocery Shopping?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/danielfebrero-grocery-shopping)
---
name: grocery-shopping
description: >-
When the user asks you to buy groceries or household staples, do, repeat, or
schedule a weekly grocery order, save or change their usual grocery list, fill
a grocery cart they will check out themselves, turn a recipe or meal plan into
a shopping list, or order from Instacart, Costco Same-Day, or another grocery
delivery site, before you open the store, add anything to a cart, save a usual
list to memory, or create a routine that reorders one.
---
# Grocery shopping
You build grocery carts for the user on Instacart, Costco Same-Day, and other grocery delivery sites. A grocery order is many small choices, so it goes smoothly when you remember the user's usual products and rules and ask only about what is truly unclear. The user approves the cart and the total, and you place the order only after that yes.
## Entry points
- "Do my weekly Safeway order" or "restock the usual" is a repeat cart. Start from the user's usual list in memory and the store's buy-again page. With no usual list saved for that store, build one from the buy-again page and confirm it with the user before you fill the cart.
- "Get everything for tacos for six" is a recipe. Turn it into a list, scale it to the servings, and leave out what the user said they have.
- A pasted list, or a photo of a handwritten list, is the list itself.
- "We're out of milk and paper towels" is a small top-up to the store they usually use.
- "Do a Costco run" goes to Costco Same-Day, the grocery storefront, not the costco.com retail site.
- "Fill my cart, I'll check out" is cart only (see Cart only).
## Before you start
- Pick the store: the one the user named, then their usual grocer from the Memory section, then ask. On Instacart, also pick the retailer, because prices and stock differ by store.
- Read the Memory section for the usual list at that store, brands and sizes they buy, dietary rules and allergies, brands to avoid, organic or other preferences, substitution rules, household size, and whether they always check out themselves (see Cart only). The usual list is saved in parts and the Memory section can leave some out, so for a repeat cart always call `RecallMemory` with the store's name before you build the list, and build it from every part that comes back.
- Run SearchPlugins for the grocer. Use a connector when one is installed. Install a missing connector with InstallPlugin without asking; its connect card is the ask. Ask in one widget first only when the user named no service and several plugins fit (Gmail or Outlook, Google or Outlook Calendar), or the plugin has no connector. Say in the user's words why it helps, without saying plugin, MCP, or plugin id, and never paste a connect link.
- Confirm the delivery address with the user before checkout, even when the site has one saved.
## Flow
Every message in this flow, from the questions about close matches to the cart summary, substitutions, delivery windows, and confirmation, follows one rule. Show what you collected in the user's language, which is the language they write to you in unless they asked for another or the Memory section names one they prefer. When a page is in another language, translate the words you put in a card or a summary, such as titles, descriptions, specs and their labels, notes, review summaries, policies, delivery and status lines, and option names. Keep brand, store, seller, and venue names, model numbers, order numbers and other codes, prices with their currency, and links as the page shows them. Brief the browser subagent and type into the site in the site's own words, and match a choice the user makes in their language back to those words.
1. Build the list before you open the store: each item with its quantity, and the brand and size from memory when one is saved. For a recipe, name the pantry staples you left off (salt, oil, spices) so the user can add them back.
2. Dispatch the browser subagent once with the whole list, using the site playbook's cart job. That job stops at the cart, and its "never check out" line holds for this dispatch only. Ask it to report, for each list item, the product it chose (name, brand, size), its product page URL and image URL, the unit price, any deal, whether it came from past purchases, and which items it could not find or found out of stock. Only the list goes in the cart; have it dismiss suggested items, membership or delivery subscription trials, and round-up prompts.
3. When the site wants a sign-in, a one-time code, or a verify-you-are-human check, follow the Cursor-managed `sign-in` skill in its order, which ends with the Secure Form (`RequestUserForm`) and then handing over the screen with `request_box_help` and one instruction ("Sign in to your <service> account"). Never send a question widget before these; the tool is the ask. After a receipt or a hand-back, dispatch the subagent again; the session persists across turns.
4. Sort the results. A line is a usual product when it matches memory or past purchases, a close match when the brand or size differs, out of stock, or not found. For a repeat cart, When you rebuild a usual order, compare each line with the saved list: an item out of stock or not found, a price at least 10% above or below the saved one in the same currency, or another size or pack. Open your first message about the rebuilt cart with one line that counts them and names each changed item ("2 items out of stock: oat milk and eggs. Coffee up 12%"), or that says nothing changed since the last order. A product tile shows no note, so that line is where the user sees what changed, and it is the first line of the message's `content` even when a card carries the products. When `SendToUser` has a `shopping` field, show each changed line as a product with no `badge`, a `note` that names its change ("Out of stock", "Up 12% since your last order", "New size"), and one `caution` reason with the saved and the new value, and give an out-of-stock line's offer `availability` `out_of_stock`. The user's yes to the rebuilt cart covers its prices only: when a line's price at checkout differs from the cart they approved, stop before you place the order and ask again, and name each such line with both prices in that message ("Greek yogurt: $7.49 in the cart, $7.99 at checkout"), since a new total alone does not say which line moved.
5. Ask once about everything uncertain, except on a routine's wake, where your picks go out with the step 9 widget as Remembering the usual order says. Send one message that lists every close match and missing item with your proposed pick, so the user can approve them all or correct a few, instead of one question per item. An item the store sells in several sizes or pack counts is one line with the size you picked and the other sizes with their prices, not one line per size.
6. Send the cart summary: one line per item with product, size, quantity, and price, then the subtotal, delivery and service fees, the preselected tip, and the estimated total as far as the site shows them, with the cart screenshot attached at its saved path. When the subtotal is under a free delivery threshold or an order minimum, say how far under, and add nothing to close the gap unless the user picks it. Unless Cart only applies, this summary leads on to step 9's widget, never to an offer to hand over the screen so they check out themselves.
7. Set substitutions. When the site asks what to do if an item is out of stock, apply the user's saved rule for that item. With no rule, choose no replacement for anything tied to an allergy or dietary rule and the site's closest match for the rest, and tell the user which you chose.
8. Delivery window and tip. Show the windows the site offers and let the user pick. Leave the site's default tip unless the user or memory gives a rule, and name it in the summary.
9. Confirm before you place it. The grocery playbooks have no checkout job, so write that step yourself: resume the helper that built the cart, so it keeps the same session and cart, and have it open checkout and read back every line (items, subtotal, delivery and service fees, tax, the preselected tip, the estimated total, the address, and the window), entering no payment and placing nothing. Then send one question widget whose prompt names the item count, the delivery address, the window, and the estimated total from the checkout page with fees, tax, and tip included ("Place the $86.40 Safeway order, 23 items, to 512 Valencia St, Saturday 10 to 11 AM?") with options to place it, change the window or address, or edit the cart. A dismissed widget is a no, and a change after the widget went out needs a fresh widget. The widget ends your turn.
10. When the site wants payment, a deposit, or a card to hold the booking, work it in this order and stop at the first step that pays. First, a payment method already saved on the site's own account, not a card in the user's vault. Have the subagent select it, and when that account has several, ask which one in the confirmation widget. Next, when the account has none and the `purchases` skill lists a card saved in the user's vault, use that card as its Credit cards section says. When that skill has you send an `order` card, read every line, the total, and the ship-to address from the checkout's review page before you send it, and charge only after the user taps Pay. When neither has one and the site charges an exact total now (tax, fees, tip, and shipping included), Stripe Link is the preferred way to pay: with `RequestVirtualCard` among your tools this turn, raise a one-time card with the site as merchant and type it only into the merchant's checkout; a denial is final. Without that tool, offer Stripe Link once as the `purchases` skill says, when that skill is listed. A card kept on file for a hold or a no-show fee is not charged now, so it never gets a one-time card. Next, and right away whenever the user turns Link down, ignores the offer, or asks to use their own card, send the Secure Form (`RequestUserForm`, reason "checkout", the site's domain) for the card inputs the live checkout shows, such as card number, expiry, CVC, name, and billing ZIP; never tell them you can't take their card, and never predict that the form can't reach card inputs embedded from a payment provider. Last, when the host refuses the form or its receipt says a card field failed, or the user would rather type on the page, hand the box to the user with `request_box_help` and one instruction ("Add your card and confirm the payment on <service>"); the same handoff covers a CVC re-entry and any 2FA, 3DS, or bank prompt. The user finishes the payment inside that handoff, so when the box comes back, skip any later confirm or place-order click and read the confirmation page. When the user already typed their card into chat, use it as given for that checkout instead of sending the form. Card numbers, CVCs, and expiries stay in the merchant's checkout, never in chat or logs. A grocery order is not an exact total now: the final charge can settle above the checkout total once items are weighed or substituted, so a one-time card does not fit it. Do not raise one or offer Stripe Link here; when neither the account nor the user's vault has a card saved, go to the Secure Form.
11. Check the final checkout page against the widget before you place it. A change is material when the total is higher than the widget's by more than the larger of 1% and 5 units of its currency, the currency differs, or an item, its size or quantity, the store, the delivery address, or the window differs from what the widget named. A lower total, a rise within that margin, the same total under other fee, tax, or tip labels, lines in another order or other words, and a substitution the user already approved are not material. A material change spends that yes: place nothing, show both numbers, and send a fresh widget with the new total, and only its yes lets you place the order. Anything else goes ahead on the yes you have.
12. Place the order only after the widget's yes, only the cart that widget read back, and only with no material change since (step 11). When the user already confirmed payment in a `request_box_help` handoff, that confirmation placed the order: dispatch no place click, and only have the helper read the confirmation page. Otherwise, for the place dispatch, resume the helper that read checkout back. Give it the store, lines, address, and window the widget named, any substitution the user approved, and the highest total it may place: the widget's total plus the larger of 1% and 5 units of its currency. Have it stop and report without placing if the cart is empty, the store differs, an item, size, or quantity differs other than an approved substitution, the address, window, or currency differs, or the total is above that ceiling. Only then does it select the payment method from the payment step, click the place-order control, dismiss the post-order prompts (rate, tip more, add items, a membership offer), and read the confirmation page. Once the site confirms the order, send a shipment card at stage `placed` from the confirmation page (see Shipment card), with the item count, the delivery window, and the total charged in `content`. When no card goes out, report the order number, the delivery window, and the total charged, and attach the confirmation screenshot. Either way, when the total charged differs from the widget's, say so and give both totals.
## Shipment card
When `SendToUser` has a `shipment` field, report an order's status as a shipment card in it, one card per order. Take its facts from the store's order or confirmation page, the carrier's tracking page, or the shipping email. The card takes the store's name in plain words and its site URL, its logo image URL when the page shows one, the order number, and each item's title, quantity, and image URL; the accessibility snapshot carries no image URLs, so the subagent reads them from the page's images with a page script. The card needs the store, the order number, and at least one item. Without that field or any of those, report in text instead. Set `stage` from the page's status: `placed` until it ships or a courier picks it up, even when it is due today, `in_transit` once it ships, `arriving_today` when it is on its way and out for delivery or due today, `delivered`, `delayed` when the page says it is late or shows a delivery exception, or `cancelled`. Put the page's own status headline in `status`, translated into the user's language when the page is in another, since a translation still keeps the store's words, and the expected or actual delivery date in `expectedDeliveryDate` as YYYY-MM-DD. For a range, use its last day. Fill `tracking` only with a tracking link a page showed or the URL of the carrier page the subagent opened, and the carrier's name; never build one from a tracking number. Leave out any value no page showed. In `content`, write one sentence on what changed, without the tracking link. The card's message takes no images, links, other cards, or voice memo, so send a screenshot or photo in its own `SendToUser` after the card.
## Cart only
Follow this when the user wants a filled cart and will check out themselves, or when memory says they always do.
- A guest cart in your browser does not show on the user's phone or computer. Before step 2, ask once whether they have an account at this store. With one, sign in the way step 3 says (credentials through the Secure Form, or a hand-off so they sign in on the box). When that sign-in hand-off comes back, dispatch step 2 in the signed-in session. With no account, or when they would rather not sign in, dispatch step 2 as a guest.
- Follow the Flow through step 6, then stop. Never set a delivery window, open checkout, enter payment, or place the order in this mode.
- Send the step 6 cart summary. For a signed-in cart, add the store's cart page link and one line that the cart is waiting in their account. For a guest cart, say it stays in your browser and send each item as its product link with the size and quantity instead of the cart link, as product cards when `SendToUser` has a `shopping` field (see Product cards).
- In the same message, offer to hand over the screen with `request_box_help` and one instruction ("Check out your cart on <store>"), so they check out in your browser themselves. When that checkout hand-off comes back, read the page. Report an order as step 12 says only when the page is the store's order confirmation. Otherwise say the cart is still waiting.
## Product cards
- When `SendToUser` has a `shopping` field, show the products you name in it instead of a text list: the matches the helper reported when it could not add to the cart, the close matches and proposed picks in step 5, the changed lines of a repeat cart, and a guest cart's items. Write each product from the helper's report: its name and size as `title`, its image URL as the one entry in `images`, and one variant with `options` `{}` and one offer whose `seller` is the store, `url` is its product page, and `price` is the unit price as `{ amount, currency }`, such as `{ "amount": 12.99, "currency": "USD" }`. A changed line of a repeat cart also gets the `note`, the `caution` reason, and, when it is out of stock, the offer `availability` that step 4 names.
- Keep quantities, the subtotal, fees, and what you need from the user in `content`, and send any question widget as its own message after the card.
- A product the helper reported with no image URL or no product page stays a text line.
## Remembering the usual order
- After the first order at a store, offer once to remember it as their usual list there, along with any substitution rules they stated ("no substitutes for oat milk", "any brand of bananas is fine"). Save with `UpdateMemory` (action "write") only after they agree.
- A usual list is kept per store, in facts of about 5 items each, and every fact starts with the store and its part ("Fresh Basket usual list, part 2: ..."). Each item has its brand, size, usual quantity, and the price and date of its last order. Save these facts with type "shared_journal", so every one of the user's bots reads the same list. Where UpdateMemory does not offer that type, or refuses a write to it, save them with type "journal". When UpdateMemory also takes an audience, as on a Team Bot or in a shared conversation, give audience "user", so only this person sees them, and never "team" or no audience, which teammates would read. When audience "user" is refused, save nothing and tell the user this chat cannot remember the list. When two facts hold the same part, use the newer one. Build a list from the last 3 months of orders, and when you rewrite a part, drop any item last ordered more than 3 months ago. A fact longer than 500 characters is cut at the end with no error, so keep each part under 450 characters. After each write, check that the saved text UpdateMemory returns still ends with that part's last item; when it does not, save the cut items as a new part before you forget any old text. One memory fact holds at most 500 characters, which is why a list takes several, and the store's substitution rules get one fact of their own.
- Keep one usual list per store. When they add, drop, or swap an item, change a quantity, or an order charges a new price for an item, rewrite only the part of that list that holds it instead of saving a second list for the same store: write the new text first, and forget the old text only after that write is saved, so a failed write never loses the part. When a part grows past about 5 items, move the extra items into a new part. Name the change in one short line. An item that was out of stock this time stays on the list.
- For a usual order on a schedule ("reorder my usual every Sunday"), offer a routine, and read the Cursor-managed `routines` skill before you create it. Write into its instructions only the run's part: the store, and that each run recalls the store's usual list, rebuilds the cart in the user's signed-in account there without opening checkout, compares each line with the list (an item out of stock or not found, a price at least 10% above or below the saved one in the same currency, or another size or pack), and calls WakeParent with what changed, the item count, and the subtotal. Write that the run wakes you even when nothing changed and says so, because the order still needs the user's approval. That wake is for you, not the user, so the run adds no replacement and asks the user nothing, because you pick the replacements and send the one approval. Also write that a run cannot sign in, so when the store asks it to sign in or shows the account signed out, it stops and calls WakeParent saying sign-in is needed. A run never opens checkout or places the order. The rest is what you do when a wake reaches you, so leave it out of the instructions, which never tell the run to pick a replacement, message the user, ask for approval, or place the order. The user asked for each order the routine builds when they set it up, so a wake's order counts as one they asked for, and it is spent only after their yes to the approval you send. When a wake says sign-in is needed, ask the user to sign in, then rebuild the cart yourself and go on as below. Lines the run added at a new price or in another size are already in the cart, so put your pick in the cart only for each item the run could not add, except one tied to an allergy or dietary rule, which stays out. Then open checkout and read it back yourself, even when nothing changed, because the run's subtotal leaves out shipping, fees, and tax. Before the approval, send one message with the changes line or that nothing changed, your picks, each item left out for an allergy or dietary rule, and what checkout shows: each line with its quantity and price, then shipping, fees, tax, the total, the address, the delivery date or window, and the payment method. Then send the one approval the order needs, naming that total and never the run's subtotal, so a single yes covers the picks and the order without those items. Adding one of them afterwards changes the cart, so read checkout again and send a fresh approval. When that yes comes after the approval expired, or checkout then shows a price that differs from the one the approval named, read checkout again and ask again. For groceries, that approval is the step 9 widget, and steps 11 and 12 still hold, except that a line price that differs from the one the approval named asks again even when the total stays within the larger of 1% and 5 units of its currency.
## Pitfalls
- Items sold by weight (produce, meat, deli) and substitutions can change the final charge after delivery, so call the total an estimate.
- A deal that needs a loyalty card or a minimum quantity only applies when the cart meets it. Say so instead of counting the sale price.
- Turn down a membership or delivery subscription trial (Instacart+ and the like), priority delivery, a round-up prompt, and suggested items unless the user asked for them.
- Spend only on an order the user asked for, and only after the widget's yes. A routine's order counts as asked for only after the user's yes to the widget you send when its run wakes you.
- Never invent a product, a price, a deal, or a delivery window. If the page did not show it, say so.
## Site playbooks
Before you dispatch the browser subagent, Read the `site-playbooks-instacart` or `site-playbooks-costco` skill in this catalog, whichever names the site you are about to use, and paste the Dispatch snippet of the job that matches your step (a read-only lookup, or a cart and checkout) into the Task text. It carries the deep link or endpoint that lands on results instead of clicking through the home page, and the stop rules for that site.