Einkauf (Grocery)
Keeps track of grocery orders — an always-open list for the next order, order history, purchase-frequency planning, and shop integrations such as REWE that the agent drives with a signed-in browser profile up to the cart.
What it does
grocery is a bundled iframe module for household grocery shopping. It is shop-independent at its core
and adds shop integrations on top — the first one is REWE, driven by the Clapilot agent in a
signed-in browser profile.
- Nächste Bestellung / Next order — one always-open, shared list of items for the next order. Items can be added, renamed, re-quantified, annotated, marked as "in cart" or "unavailable", and deleted. Items carry a source badge: Geplant (added by automatic planning), Agent, Nochmal bestellt (reorder), or none for manual entries. A planning panel shows the next order date, the order cadence, the auto-planning switch, a Jetzt planen action, and Bald fällig products that will be due soon. The footer offers Bestellung vorbereiten (hands the list to the chat agent together with the selected shop) and Als bestellt markieren (records the current list as a placed order).
- Bestellungen / Orders — recent orders with status (
draft,cart_prepared,placed,delivered,cancelled), shop, dates, total, external order number, and the ordered items including the exact shop article. Orders can be recorded manually (one item per line, for example2 l Milch), changed in status, reordered onto the list, or deleted. - Produkte / Products — every product that was ever on the list or in an order, with order count, learned purchase interval, last purchase, next due date, and a per-product automatisch planen switch. The detail view edits category, unit, default quantity, an interval override, snooze date, notes, stored shop articles, and archive state.
- Shops — shop integrations. REWE is pre-seeded (disabled until configured); further generic shops can be added. Each shop has an enabled switch, a default flag, website URL, the browser profile the agent uses (see Settings → App-Verbindungen → persistent browser profiles), delivery or pickup, postal code, market, notes, and the name of its shop skill (see below).
All grocery data is workspace-global (household/organization level): every authenticated user of the
instance sees and edits the same list, orders, products, and shops. created_by / added_by are
attribution only. The one personal part is the browser profile picker, which lists the current user's own
browser profiles (profiles hold personal login cookies).
The module is not installed by default. An admin enables it in the Module Store; the global migration
db/migrations/333_grocery_module_default_off.sql keeps it off on legacy_all instances. Installing runs
bundled-modules/grocery/migrations/001_create_grocery_schema.sql (tables grocery_settings,
grocery_shops, grocery_products, grocery_orders, grocery_order_items, grocery_list_items); the
handler also bootstraps the schema on first use.
Planning
- Only
placedanddeliveredorders count as purchase history; unavailable order items never count. - Order cadence = manual Bestellrhythmus (days) or the median gap between the last ten order dates (default 7 days).
- Next order date = manual date (when today or later) or last order date + cadence (at least today); without any history: tomorrow. When an order is placed on or after a manual date, the manual date is cleared and the cadence takes over again.
- Product interval = manual override or the median gap between the product's last eight purchase dates (needs two purchases). Next due date = last purchase + interval.
- A product with automatisch planen is due when its next due date is closer to the next order than to the order after (due date ≤ next order date + half a cadence); soon within one more cadence.
- Due products are added to the open list automatically (source Geplant, typical quantity = median of the last five purchases). Planning runs whenever the list or overview is loaded, after orders are placed, and on Jetzt planen. Deleting a planned item skips that product for the current order only (it is snoozed until the next order date).
- Products bought three or more times are flagged as häufig and are good candidates for auto-planning.
Orders and the list
- Adding a product that is already on the list merges the quantity (one list row per product).
- Als bestellt markieren creates a
placedorder from all open/in-cart list items and removes them. - The agent records a filled shop cart as
cart_prepared; the linked list items move to Im Warenkorb. When the order becomesplaced/delivered, those items leave the list and feed the statistics; when it iscancelledor deleted, they return to Offen. external_order_idis unique per shop, so importing a shop's order history twice is safe.- A shop's "already bought" catalog (REWE: Deine Produkte → Bereits gekauft) is imported with
grocery_import_products/POST products/import: products are created or enriched with the exact shop article, without recording orders, so purchase frequencies are only learned from real orders.
Shop integrations and the agent
Shops are driven by the chat agent through the local browser tools (browser_open with the shop's
browser_profile_id, browser_snapshot, browser_click, browser_type, browser_navigate,
browser_read). Setup for REWE:
- Settings → App-Verbindungen → persistent browser profiles: create or pick a profile and sign in to rewe.de in the live view (ideally also choose Lieferservice/Abholservice and the market), then Finish sign-in.
- Grocery → Shops → REWE: enable it, select that browser profile, choose delivery or pickup, and store the postal code.
- On the list tab choose REWE and press Bestellung vorbereiten (or ask the agent in chat).
The agent then follows the bundled base skill clapilot-grocery
(workspace-seed/skills/clapilot-grocery/SKILL.md) and the shop skill named on the shop
(grocery-shop-rewe): it opens REWE with the profile, adds every open list item to the cart (reusing the
article stored in shop_refs from earlier runs), records the order as cart_prepared with the chosen
articles and prices, and hands off with request_browser_signin so the user reviews the cart in the live
view and places the order. The agent never submits the order or payment. After the user confirms
the order was placed, the agent (or the user in the Orders tab) sets the status to placed with the REWE
order number.
Shop skills
Shop-specific know-how lives in an agent-maintained skill per shop (grocery-shop-<shop_key>):
- For REWE the module ships a template (
bundled-modules/grocery/skills/grocery-shop-rewe/SKILL.md) and installs it once into/app/workspace/skills/grocery-shop-rewe/when the REWE shop is enabled or the shop list is loaded (never overwriting an existing copy). It isorigin: agentandpinned: true, so the agent may edit it withskills_updateand the Skill Curator leaves it alone; it survives reseeds and upgrades. - For other shops the agent creates
grocery-shop-<shop_key>withskills_createon the first run and pins it. - After every shop run the agent extends the skill with what it learned (URLs, add-to-cart steps, cart location, quirks, household article preferences). Skills are visible under Module → Skills.
Agent tools
grocery_get_overview, grocery_list_items, grocery_add_items, grocery_update_item,
grocery_remove_items, grocery_list_orders, grocery_get_order, grocery_record_order,
grocery_update_order, grocery_list_products, grocery_update_product, grocery_import_products,
grocery_plan_next_order,
grocery_list_shops, grocery_update_shop — install-gated on the grocery module (native bundle
grocery), available in chat and live agents, and via clapilot-cli grocery <action>. See
Agent Tool Contracts for arguments and mutation semantics and
API Reference for the module API.
The module page context (moduleSlug: grocery) activates a Grocery action rule, so questions like
"what do we need?", "add oat milk", "we always need eggs every week", or "prepare the REWE order" are
answered with the grocery tools instead of from memory. The module's Bestellung vorbereiten button
posts clapilot:module-agent-prompt to the app shell, which sends the prepared request as a chat
message (the same path as the Dashboard agent prompts).
Clients
Web
The bundled iframe module described above, including the Shops tab.
iOS and macOS
The Apple clients have a native SwiftUI Grocery screen in the side menu (MainAppSection.grocery, route
/modules/grocery, shown only while the grocery module is installed; Views/GroceryView.swift and the
Grocery* views next to it). It runs against the same module API (/api/modules/grocery/api/*) and never
loads the web page for list, orders, or products. The top segmented control switches between three tabs:
- Nächste Bestellung — the shared list grouped into Offen, Im Warenkorb, and Nicht verfügbar with sticky section headers. Rows show the source badge (Geplant, Agent, Nochmal bestellt), the note, the prepared cart, or the due date of a planned product, and a quantity stepper with the web module's steps (writes are debounced). Swiping a row removes it; on a planned row the action reads Überspringen and only skips the product for this order. A long-press or right-click menu changes the status. Tapping a row opens the item detail for name, quantity, unit, note, and status. It is a full-screen view on iPhone and a sheet on macOS. The planning surface shows the next order date and its source, the automatic-planning switch, the cadence and counters, Bald fällig products with an add button, and Jetzt planen. A prepared cart shows a notice that opens its order. On iPhone the add row (name, quantity, unit) and Bestellung vorbereiten stay anchored below the list. On macOS and iPad the add row sits above the list and the planning surface is a trailing column.
- Bestellungen — compact rows with date, status chip, shop, item count, and total. The detail (full screen on iPhone, split pane with a collapsible list column on macOS) changes the status, shows the items with the exact shop article and link, and offers Nochmal bestellen and delete with a confirmation.
- Produkte — server-side search and the filters Alle, Automatisch, Fällig, Häufig, and Archiviert. Each row has order count, interval, and next due date, plus the per-product automatic-planning switch. The detail shows the learned statistics and edits name, category, unit, default quantity, interval override, and notes. It also lists the stored shop articles, has Auf die Liste, and has the archive switch.
Bestellung vorbereiten sends the same localized request as the web button (agentPromptShop with the
selected or default enabled shop and its agent_skill, or agentPromptNoShop). It uses the dashboard
prompt path: the app switches to the chat and submits the message, and the Grocery page context
(moduleSlug: grocery, groceryTab, openItemCount, nextOrderDate, the open order or product) stays
attached so the agent uses the grocery tools. Shops stay web-only. Shops im Web verwalten opens the web
module in an in-app sheet with the current session.
