Food ingest
Ingest a venue from a Reel, Instagram, Facebook, or YouTube post into a trip PWA's food, desserts, attractions, fandom, or nearby corpus (or feed_candidates.json when placement is unclear). Use when the user shares a venue video/post URL or caption and wants it added to their trip. Fetches the caption, classifies it via the shared router, supports an explicit --to corpus override, and writes a structured entry. Pairs with trip-scaffold and refs-ingest.From its SKILL.md
npx -y skills add fantasybz/trip-pwa-skills --skill food-ingestAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 27 days oldThe repository was created 27 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.3 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
food-ingest
Turn a short-form venue post into a structured entry in the right trip-PWA
corpus, or hold it in feed_candidates.json until its placement is confirmed.
Despite the name, food-ingest handles all five venue corpora (food / desserts /
attractions / fandom / nearby). The hard part is placement, not classification:
a caption tells you "this is a cake cafe" but not "this goes on Day 3's 14:00
anchor". So food-ingest routes by keyword and auto-routes a confident item
straight to its corpus file — food.json, desserts.json, attractions.json,
fandom.json, or nearby.json (v0.5.1). Only ambiguous items (tie / low
confidence / no keyword) land in feed_candidates.json for a one-command
placement-promote (the deferred-placement pattern). --day carries a known day;
--to <corpus> is the destination-neutral explicit override; legacy
--force-food still forces food.json.
Steps
-
Get the caption. If the user gave a URL, fetch the caption/description first (use /browse for the page text, or
yt-dlp --get-descriptionfor the video description; for audio-only Reels,yt-dlp+whisper-clito transcribe). If the user pasted a caption, use it directly. The caption is what the router classifies — without it, routing is blind. -
Run the engine with the caption + any known fields:
bun skills/food-ingest/food-ingest.ts --out <trip-dir> \ --caption "<fetched caption>" --name-zh "<venue name in zh>" \ [--url <source>] [--day day_2] [--anchor shibuya] \ [--to food|desserts|attractions|fandom|nearby] \ [--category ramen] [--why "<1-line note>"] [--kid-friendly true] \ [--name-jp "<destination-local name>"] \ [--address "<street address>"] [--hours "11:00-21:00"] [--price "₩₩"] \ [--maps-query "<name + area for a Maps search>"]The last four are optional but make the entry useful on the ground: the food view renders address/hours/price as text (the offline fallback) and a 📍 地圖 link built from
--maps-query(preferred) or--address. Pull these from the post/caption when present — a name + Reel link alone isn't navigable. -
Report the outcome. The engine prints which corpus each item auto-routed to (
<corpus>.json) or that it landed in 待分類 (with the exactplacement-promote ... --to <corpus>next-step command), and why. Relay that.
Routing decision (shared _lib/router.ts)
route(caption)returns{ corpus, confidence, reasons, tied_with? }, wherecorpusisnullwhen no keyword matched (needs human review).- →
<corpus>.json(auto-route, v0.5.1) whencorpusis non-null ANDconfidence >= MIN_CONFIDENCE (0.4)AND notied_with— the item is written straight to its corpus file (food / desserts / attractions / fandom / nearby).--to <corpus>wins over the router and is the preferred correction for geography-specific ambiguity;--force-foodremains a food-only alias. Entry shape comes from_lib/venue-entry(food keeps the full shape; non-food corpora get the generic subset — no food-only fields), shared withplacement-promoteso direct-ingest and promote produce identical entries. - → feed_candidates.json otherwise (tie / low confidence /
null), tagged withcandidate_for,confidence,tied_with,reasons, and aday_hintif--daywas given. The venue view shows these inline tagged 待分類; promote a confirmed one with theplacement-promoteskill (--id <id> --to food). Do not re-run the same source URL + venue name with--to: normal ingest dedup treats that pair as an existing item and skips it. Candidates retain every supplied author field while placement is unresolved. Promotion tofoodpreserves the full set; promotion to a non-food corpus preserves every field in that corpus's generic target schema and deliberately omits food-only fields. --dayno longer forces a non-food caption intofood.json. It only binds a day: a confident non-food item still routes directly to its corpus with that day inday_keys; an ambiguous item becomes a candidate carryingday_hint. Use--to <corpus>to correct placement explicitly, or legacy--force-food(with--category) for a genuine food spot the router misclassified.
Batch ingest (--batch)
For multiple posts, pass a JSON array file — the engine reads all five venue
corpora plus feed_candidates.json ONCE, validates and deduplicates against the
whole set, writes each changed file ONCE, and regenerates the service worker
ONCE (batch-aware, design doc D5; avoids 12× rewrite/rehash):
bun skills/food-ingest/food-ingest.ts --out <trip-dir> --batch items.json
# items.json = [{
# "caption": "...", "name_zh": "...", "name_jp_or_local": "...",
# "url": "...", "day": "day_2", "to": "food", "anchor": "...",
# "category": "...", "why_picked": "...", "kid_friendly": true,
# "backup_fit": "...", "address": "...", "hours": "...", "price": "...",
# "maps_query": "..."
# }, ...]
The underscored keys above are preferred in batch JSON; legacy dashed forms
(kid-friendly, name-jp, backup-fit, maps-query) also work. A duplicate is
only the same source_url and venue name (multi-venue posts share URLs);
duplicate ids get a -2/-3 suffix. URL-less items use a durable ID derived
from the full normalized authoring input, including the caption and requested
destination. Re-running the exact same URL-less input after JSON committed but
SW regeneration failed finds the same semantic row, skips a duplicate, and
repairs the manifest. A different caption remains a distinct item even when its
persisted venue fields happen to match; the same derived ID with different data
fails closed and points corrections to placement-promote.
A malformed venue corpus or feed_candidates.json is never overwritten — the
engine validates every input file it reads and tells you to fix or remove it.
Placement model (design doc D8 — resolved in v0.2)
The five venue corpora can carry schedule placement (day / anchor / time), but a
caption cannot reliably infer that context. food-ingest accepts this: a
high-confidence single-venue post lands directly in food.json,
desserts.json, attractions.json, fandom.json, or nearby.json; anything
ambiguous waits in feed_candidates.json. The A2 dogfood confirmed placement
was the dominant friction, so v0.2 shipped the fallback: the venue view renders
candidates inline (tagged 待分類), and placement-promote moves a confirmed item
into the chosen corpus — no data is invisible while it waits.
Multi-venue posts
One post can list many venues (e.g. "東京拉麵 5 選"). Call the engine once per
venue you want to ingest, each with its own --name-zh (+ --caption scoped to
that venue's line if you can). Uniqueness is per-entry, so re-running for each
venue is the intended pattern.
What ships with it: 2 files
35.8 KB alongside SKILL.md, 2 of them executable
- food-ingest.test.tsruns16.5 KB
- food-ingest.tsruns19.3 KB
Gives 0 of the 12 instructions most video audio skills give in ~1.8k tokens
Counted across 622 of the 795 authors here whose files we hold, read 2026-08-07
- Read individual rule files for detailed explanationsin 21 of 622, across 10 files
- Render final videoin 13 of 622, across 6 files
- Use WAV PCM 16kHz mono audio formatin 12 of 622, across 3 files
- Use this skill when dealing with Remotion codein 11 of 622, across 4 files
- Save generated audio to a WAV filein 11 of 622, across 4 files
- Handle conversion errors gracefullyin 10 of 622, across 6 files
- Add captions to videos alwaysin 10 of 622, across 4 files
- Generate music from text descriptions using MusicGenin 9 of 622, across 2 files
- Do not skip pipeline layersin 9 of 622, across 3 files
- Do not make one tool do everythingin 9 of 622, across 3 files
- Use Azure Document Intelligence for complex PDFsin 9 of 622, across 4 files
- Never ask the user to paste their full API keyin 9 of 622, across 3 files
Said here and by no other author read
- fetch caption text before classifying
- pass caption and known fields to the engine
- include address hours price and maps query when available
- report the routing outcome and next-step command
- use explicit override to correct placement
- pass a json array file for batch ingest
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.