Use this skill to verify whether restaurants actually have Planted products on their current menu. Crawls restaurant websites, parses PDF menus, and updates venue verification status in Firestore.
This skill verifies whether discovered venues actually have Planted products on their current menus. It's essential for maintaining data quality - press releases and discovery don't guarantee ongoing availability.
Triggers on:
unverifiedcd planted-availability-db/packages/scrapers
# Verify all unverified venues with websites
node verify-menu-agent.cjs
# Verify a specific venue by ID
node verify-menu-agent.cjs --venue-id=VENUE_ID_HERE
| Status | Meaning |
|---|---|
verified_on_menu | Planted products confirmed on current menu |
not_found_on_menu | No Planted products found after thorough check |
unverified | Not yet checked |
needs_manual_check | Website couldn't be crawled, needs human verification |
For venues the agent can't verify automatically:
# Get venue details
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
db.collection('venues').doc('VENUE_ID').get().then(d => console.log(JSON.stringify(d.data(), null, 2)));
"
Common PDF locations to check:
/media/menu.pdf, /media/speisekarte.pdf/downloads/menu.pdf/restaurant/menu.pdf_en.pdf, _de.pdf, _d.pdfIn PDFs or HTML, search for:
planted (brand name)plant-based, plant based, pflanzenbasiertgeschnetzeltes (common planted dish)planted chicken, planted kebab, planted steak# Mark as verified with planted products
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
db.collection('venues').doc('VENUE_ID').update({
status: 'active',
verification_status: 'verified_on_menu',
verification_date: new Date(),
verification_method: 'manual',
verification_note: 'Found X planted dishes on menu PDF',
updated_at: new Date()
});
"
# Or mark as not found
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
db.collection('venues').doc('VENUE_ID').update({
status: 'unverified',
verification_status: 'not_found_on_menu',
verification_date: new Date(),
verification_method: 'manual',
verification_note: 'No planted products found on menu',
updated_at: new Date()
});
"
When planted products are found:
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
const dish = {
venue_id: 'VENUE_ID',
name: 'Planted Geschnetzeltes mit Rösti',
description: 'Swiss-style planted strips with hash browns',
price: { amount: 32.00, currency: 'CHF' },
planted_products: ['planted.geschnetzeltes'],
status: 'active',
source: 'menu_verification',
created_at: new Date(),
updated_at: new Date()
};
db.collection('dishes').add(dish).then(ref => console.log('Added dish:', ref.id));
"
Common product identifiers:
planted.chicken - Planted Chickenplanted.geschnetzeltes - Planted Geschnetzeltes (strips)planted.kebab - Planted Kebabplanted.steak - Planted Steakplanted.pulled - Planted Pulledplanted.schnitzel - Planted SchnitzelIf a venue doesn't have planted products and was added in error:
# Delete venue (and its dishes)
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
const venueId = 'VENUE_ID';
// Delete dishes first
db.collection('dishes').where('venue_id', '==', venueId).get()
.then(snap => {
const batch = db.batch();
snap.docs.forEach(doc => batch.delete(doc.ref));
return batch.commit();
})
.then(() => db.collection('venues').doc(venueId).delete())
.then(() => console.log('Deleted venue and dishes'));
"
The verify-menu-agent.cjs searches for these patterns:
const SUBPAGE_PATTERNS = [
'/restaurant', '/en', '/de', '/bar', '/cuisine', '/kulinarik'
];
const COMMON_PDF_PATHS = [
'/media/menu.pdf', '/media/speisekarte.pdf',
'/media/restaurant_mo_-_fr_d.pdf', // Kronenhalle pattern
'/downloads/menu.pdf', '/menu.pdf'
];
const PLANTED_KEYWORDS = [
'planted', 'plant-based', 'plant based', 'pflanzenbasiert',
'planted chicken', 'planted kebab', 'planted geschnetzeltes'
];
The agent requires:
npm install pdf-parse # For PDF text extraction
$ node verify-menu-agent.cjs
=== Menu Verification Agent ===
Found 3 unverified venues with websites
Checking: Kronenhalle (https://www.kronenhalle.com)
Crawling homepage...
Found 2 PDF links
Parsing: restaurant_mo_-_fr_d.pdf (12 pages)
FOUND: "Planted Geschnetzeltes «Kronenhalle» mit Rösti"
FOUND: "Planted Steak mit Sanddorn"
Status: verified_on_menu (2 dishes added)
Checking: Lindenhofkeller (https://www.lindenhofkeller.ch)
Crawling homepage + subpages...
Found 3 PDF links
Parsing: drinks.pdf (31 pages) - no planted
Parsing: lunch.pdf (4 pages) - no planted
Parsing: dinner.pdf (6 pages) - no planted
Status: not_found_on_menu
=== Summary ===
Verified: 1
Not found: 1
Errors: 0
npm ls pdf-parse