Resources
7Install
npx skillscat add simhacker/moollm/skills-groceries Install via the SkillsCat registry.
SKILL.md
๐ GROCERIES
"Tea. Earl Grey. Hot. And add milk to the replicator queue."
Dutch supermarket integration with smart shopping automation.
Sister scripts for Albert Heijn API, meal planning, and list management.
Quick Start
# Anonymous (no login needed)
python scripts/ah.py search "hagelslag"
python scripts/ah.py bonus --week current
python scripts/ah.py stores --near "Amsterdam"
# Authenticated (requires 1Password setup)
python scripts/ah.py login
python scripts/ah.py receipts --limit 10
python scripts/ah.py orders
python scripts/ah.py list add "melk" "brood" "kaas"Architecture
PUBLIC โ The tools (shareable):
moollm/skills/groceries/ # THIS REPO โ Share freely
โโโ CARD.yml # Quick reference
โโโ SKILL.md # Protocol docs
โโโ scripts/
โ โโโ ah.py # Albert Heijn API client
โโโ templates/ # Config templates
โ โโโ config.yml.tmpl
โโโ examples/ # ๐ Trekified examples
โโโ enterprise-galley.yml # TNG: Ship's galley
โโโ ds9-replimat.yml # DS9: Supply crisis
โโโ voyager-neelix.yml # VOY: Neelix's kitchen
โโโ klingon-kitchen.yml # Warrior's guide to cookingPRIVATE โ Your data (your repo or local):
YourHome/household/shopping/ # YOUR REPO โ Personal data
โโโ INDEX.yml # Master index
โโโ current-list.yml # Today's shopping list
โโโ grocery/
โ โโโ ah-cache.yml # Product cache, favorites, deals
โ โโโ meal-planning.yml # Your recipes (English + Dutch terms)
โ โโโ albert-heijn.yml # Order history (if scraping)
โ โโโ delivery-apps.yml # What you order (for recipe ideas)
โโโ zooplus.yml # Cat supplies (separate tracking OK)The Split:
| PUBLIC (skill) | PRIVATE (your repo) |
|---|---|
| ah.py script | Your product cache |
| API documentation | Your price tracking |
| Trek examples | Your deal alerts |
| Generic templates | Your shopping lists |
| How-to guides | Your order history |
| Recipe structures | Your actual recipes |
Private Cache Usage
Your private repo stores the actual data. The public skill provides tools.
ah-cache.yml (Your Repo)
Track products you buy, prices, and deals:
# YourHome/household/shopping/grocery/ah-cache.yml
regulars:
mexican_essentials:
items:
- product: "AH Rundergehakt"
webshop_id: 123456 # From: ah.py search "rundergehakt" --json
usual_qty: "750g"
frequency: "weekly"
bonus_tracking:
watch_list:
- "Rundergehakt"
- "Tortilla wraps"
- "Avocado's"
current_deals:
week: 5
relevant:
- "Rundergehakt 500g: โฌ4.49 (was โฌ5.49)"
price_reference:
rundergehakt_500g:
normal: "~โฌ5.50"
good_deal: "<โฌ4.50"Workflow
# 1. Check this week's bonus deals
python ah.py bonus --week current
# 2. Search for products you need
python ah.py search "rundergehakt"
# 3. Update your ah-cache.yml with:
# - Product IDs (for list sync)
# - Current bonus prices
# - Price history
# 4. Generate shopping list based on:
# - Weekly meal plan
# - What's on bonus
# - What's running lowCredentials โ 1Password Integration
Store your supermarket credentials in 1Password, retrieve via op:
# Setup (one time)
op signin
# The script uses:
op read "op://Personal/Albert Heijn/email"
op read "op://Personal/Albert Heijn/password"Config Template
Copy templates/config.yml.tmpl to ~/.moollm/skills/groceries/config.yml:
# ~/.moollm/skills/groceries/config.yml
# GITIGNORED โ contains personal data
credentials:
method: "1password" # or "env" or "direct"
# 1Password paths
op:
ah:
email: "op://Personal/Albert Heijn/email"
password: "op://Personal/Albert Heijn/password"
jumbo:
email: "op://Personal/Jumbo/email"
password: "op://Personal/Jumbo/password"
# Or environment variables
# env:
# ah_email: "AH_EMAIL"
# ah_password: "AH_PASSWORD"
preferences:
default_store: "ah"
delivery_address: "Your address here"
# Dietary preferences for suggestions
diet:
vegetarian: false
vegan: false
gluten_free: false
lactose_free: false
# Favorite brands (for SUGGEST)
brands:
coffee: ["Douwe Egberts", "Lavazza"]
cheese: ["Old Amsterdam", "Beemster"]Albert Heijn API
Endpoints
| Endpoint | Auth | Description |
|---|---|---|
/mobile-auth/v1/auth/token/anonymous |
No | Get anonymous token |
/mobile-auth/v1/auth/token |
Login | Get user token |
/mobile-services/product/search/v2 |
Anon | Search products |
/mobile-services/v1/receipts |
User | Get receipts |
/mobile-services/v2/receipts/{id} |
User | Get receipt details |
/mobile-services/shoppinglist/v2/items |
User | Manage shopping list |
/gql |
Varies | GraphQL (bonus, categories) |
Headers Required
User-Agent: Appie/8.22.3
Content-Type: application/json
Authorization: Bearer {access_token} # for authenticated requestsAuthentication Flow
1. Visit: https://login.ah.nl/secure/oauth/authorize?client_id=appie&redirect_uri=appie://login-exit&response_type=code
2. Login with credentials
3. Get redirected to: appie://login-exit?code=CODE
4. Exchange code for token:
POST https://api.ah.nl/mobile-auth/v1/auth/token
{"clientId": "appie", "code": "CODE"}
5. Receive: {"access_token": "...", "refresh_token": "...", "expires_in": 7199}Sister Script: ah.py
Structure (follows sister-script pattern)
#!/usr/bin/env python3
"""Albert Heijn API client โ groceries skill sister script."""
import argparse
import json
import subprocess
from pathlib import Path
from dataclasses import dataclass
from enum import Enum
import requests
# CONFIGURATION
CONFIG_PATH = Path.home() / ".moollm/skills/groceries/config.yml"
TOKEN_CACHE = Path.home() / ".moollm/skills/groceries/.tokens.json"
API_BASE = "https://api.ah.nl"
USER_AGENT = "Appie/8.22.3"
# STATE
@dataclass
class AuthState:
access_token: str | None = None
refresh_token: str | None = None
expires_at: float = 0
# CLI DEFINITION
def create_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Albert Heijn API client",
epilog="See 'ah.py COMMAND --help' for details."
)
sub = parser.add_subparsers(dest="command")
# SEARCH โ Find products
search = sub.add_parser("search", help="Search for products")
search.add_argument("query", help="Search term")
search.add_argument("--limit", type=int, default=10)
search.add_argument("--bonus", action="store_true", help="Only bonus items")
# BONUS โ Get current deals
bonus = sub.add_parser("bonus", help="Get bonus/sale items")
bonus.add_argument("--week", default="current")
bonus.add_argument("--category", help="Filter by category")
# STORES โ Find stores
stores = sub.add_parser("stores", help="Find stores")
stores.add_argument("--near", help="Location (city or coordinates)")
stores.add_argument("--limit", type=int, default=5)
# LOGIN โ Authenticate
sub.add_parser("login", help="Login to AH account")
# RECEIPTS โ Get purchase history
receipts = sub.add_parser("receipts", help="Get receipts")
receipts.add_argument("--limit", type=int, default=20)
receipts.add_argument("--detail", help="Get specific receipt ID")
# ORDERS โ Get online orders
orders = sub.add_parser("orders", help="Get online order history")
orders.add_argument("--limit", type=int, default=10)
# LIST โ Manage shopping list
list_cmd = sub.add_parser("list", help="Manage shopping list")
list_sub = list_cmd.add_subparsers(dest="list_action")
list_sub.add_parser("show", help="Show current list")
list_add = list_sub.add_parser("add", help="Add items")
list_add.add_argument("items", nargs="+", help="Items to add")
list_clear = list_sub.add_parser("clear", help="Clear list")
return parser
# IMPLEMENTATION
# (See full script in scripts/ah.py)Methods
SEARCH โ Find Products
invoke: python scripts/ah.py search "kaas" --limit 5
example_output:
- title: "AH Jong belegen kaas plakken"
price: 2.49
unit: "200g"
bonus: true
bonus_price: 1.99BONUS โ Current Deals
invoke: python scripts/ah.py bonus --week current
# Or via GraphQL for full details:
query: |
query bonusCategories($input: PromotionSearchInput) {
bonusCategories(filterSet: WEB_CATEGORIES, input: $input) {
id
title
promotions {
title
price { now { amount } was { amount } }
}
}
}LIST-SYNC โ Sync to AH App
invoke: python scripts/ah.py list add "melk" "brood" "eieren"
# Adds to AH shopping list via API:
# PATCH /mobile-services/shoppinglist/v2/items
# {"items": [{"productId": 123, "quantity": 1, "type": "SHOPPABLE"}]}RECEIPTS โ Purchase History
invoke: python scripts/ah.py receipts --limit 5
example_output:
- transaction_id: "ABC123"
date: "2026-01-28"
store: "AH Amsterdam Centrum"
total: 45.67
items: 23ANALYZE โ Pattern Analysis
# After fetching receipts, analyze patterns:
patterns:
most_bought:
- "AH Halfvolle melk": 47 times
- "AH Volkoren brood": 43 times
- "Avocado": 38 times
weekly_spend:
average: "โฌ127.50"
min: "โฌ45.00"
max: "โฌ210.00"
shopping_days:
saturday: 45%
sunday: 30%
wednesday: 15%
bonus_usage:
percent_bonus_items: "34%"
estimated_savings: "โฌ45/month"Trekified Examples
All examples use Star Trek terminology for safe public sharing:
Enterprise Galley (examples/enterprise-galley.yml)
# USS Enterprise NCC-1701-D โ Deck 10 Forward Galley
# Trekified grocery data for safe sharing
ship: "๐USS Enterprise NCC-1701-D"
location: "๐Deck 10, Forward Section"
galley_chief: "๐Lieutenant Commander Data (acting)"
replicator_queue:
protein:
- item: "๐Replicated protein base (bovine)"
english: "Ground beef"
qty: "750g"
produce:
- item: "๐Vulcan root vegetable"
english: "Onion"
qty: 2
- item: "๐Risan bell fruit"
english: "Bell pepper"
qty: 3
dairy:
- item: "๐Dairy matrix Type-7"
english: "Sour cream"
qty: 2
priority: CRITICAL
note: "๐Captain's standing order"
commissary:
preferred: "๐Starbase 375 Commissary"
backup: "๐Deep Space 9 Promenade"
account:
officer: "๐Captain Picard"
contact: "๐picard@starfleet.fed"DS9 Replimat (examples/ds9-replimat.yml)
# Deep Space Nine โ Promenade Replimat
# Quark's complaint: "The replicators are inferior to real food!"
station: "๐Deep Space Nine"
establishment: "๐Replimat (Promenade, Level 1)"
manager: "๐Ensign Recurring Background Character"
inventory_issues:
always_out_of:
- "๐Altarian mineral water (lime)" # Jarritos
- "๐Bajoran spring wine"
- "๐Cardassian yamok sauce"
overstocked:
- "๐Replicated tube grubs"
- "๐Synthesized gagh (dead)"
regular_orders:
major_kira:
usual: "๐Raktajino, extra strong"
frequency: "3x daily"
odo:
usual: "Nothing (doesn't eat)"
frequency: "Judges others"
quark:
usual: "Real food, not replicated garbage"
source: "๐Personal suppliers (don't ask)"Integration with Your Repo
The groceries skill works with your personal MOOLLM repo:
# In your repo: household/shopping/grocery/INDEX.yml
meta:
skill: groceries
integration:
config: "~/.moollm/skills/groceries/config.yml"
scripts: "moollm/skills/groceries/scripts/"
# Your real data stays in your repo
# Skill provides the automationResources
Libraries
| Language | Package | Status |
|---|---|---|
| Go | github.com/gwillem/appie-go |
Active (Jan 2026) |
| Node.js | albert-heijn-wrapper |
Active |
| Python | skills/groceries/scripts/ah.py |
This skill |
Documentation
- API Gist:
gist.github.com/jabbink/8bfa44bdfc535d696b340c46d228fdd1 - GraphQL Schema:
github.com/gwillem/appie-go/doc/graphql-schema-20260118.md - OpenAPI Spec:
github.com/NickBouwhuis/Albert-Heijn-OpenAPI
Community
- Price Comparison:
lijssie.nl(AH, Jumbo, Dirk, Coop, etc.) - Gist Comments: Active discussion on jabbink's gist
See Also
skills/sister-scriptโ Script structure patternskills/trekifyโ Privacy through technobabbleskills/inventoryโ General inventory tracking