Skip to content

Repository files navigation

Fondo

A personal meal-prep app: structured recipes and ingredients instead of manually re-deriving a shopping list every week, with Picnic as the delivery layer.

Built for two people (me + my household), as a portfolio piece and a hands-on lab for AI PM judgment and agentic dev practices (RPI/QRSPI).

Not a production app — no public access, no accounts, no reliability guarantees on the Picnic integration (unofficial API).

Status

Phase 1 complete. Recipes/ingredients as structured data, a meal-plan week view, a shopping-list page generated from that plan, and a real push to the Picnic basket — all through the app's own UI, no scripts involved. Confirmed end-to-end against a real account: a matched item ("Harry Sammys Super Sandwich") landed in the real Picnic basket via the app's push button. See docs/phases.md for what shipped when.

Design system (Apple-inspired, light/dark) is applied throughout. Picnic auth uses a stored PICNIC_AUTH_KEY — see "Security notes" below before treating that as a solved problem.

Docs

Source of truth for scope and direction lives in Obsidian, not here:

04 After the Algorithm Lab/06 Prototypes & Apps/Meal Prep App/Vision.md

Tech stack

One language end-to-end, chosen for a laptop-only, two-person app with no auth and no scale requirements:

  • Next.js 16 (App Router, TypeScript) — pages are server components that read the database directly; mutations go through Server Actions. Next 16 changed several APIs since prior training data (async params, Server Actions patterns) — see AGENTS.md / node_modules/next/dist/docs before assuming older Next.js conventions.
  • Prisma 7 + SQLite (better-sqlite3 driver adapter) — one file database (dev.db, gitignored), good enough for two users and no need for a server-hosted DB. Prisma 7 changed generator config significantly (prisma-client generator, driver adapters mandatory, prisma.config.ts for connection config) — see prisma.config.ts and prisma/schema.prisma.
  • Tailwind CSS — utility styling, no component library yet.
  • picnic-api (unofficial Node wrapper) — used in src/lib/picnic.ts for product search and cart pushes. Auth works via a stored PICNIC_AUTH_KEY in .env, obtained once via scripts/picnic-login.ts (+ scripts/picnic-verify-2fa.ts if Picnic requires 2FA, which it did on first login — can't be completed from a server request, only from that standalone script).
  • Anthropic SDK — not added yet; planned for Phase 3 (chat-assisted planning).

Structure

prisma/schema.prisma   Data model: Recipe, Ingredient, RecipeIngredient,
                        MealPlan, MealPlanItem, List, ListItem
src/lib/db.ts           Prisma client singleton
src/lib/picnic.ts       Picnic client wrapper (search, cart)
src/lib/shopping-list.ts  Aggregates ingredient quantities across recipes,
                        scaled from each recipe's base portions
src/app/recipes/        Recipe list + create form
src/app/plan/           Meal-plan week view — assign recipes to days
src/app/shopping-list/  Generates from the plan, Picnic matching + push
src/app/lists/          Generic lists (household staples, etc.)
src/lib/week.ts          Week math shared by /plan and /shopping-list
scripts/picnic-*.ts     One-off spikes: login/2FA, then search+add-to-cart.
                        Not part of the app; run with `npx tsx scripts/...`

How phases map to this repo

Phases were renumbered 2026-07-23 — Phase 1 shipped thinner than the vision doc assumed, and refinement moved up from a vague Phase 4 into a concrete Phase 2, ahead of the chat feature. See Vision.md for the full note.

  • Phase 1 (done): recipes/ingredients, meal planning, shopping-list generation, ingredient→Picnic product matching (manual confirm, no auto-accept), and the real basket push. See docs/phases.md for what shipped when this pass (bulk auto-matching, editing items already in the Picnic cart, out-of-stock handling).
  • Phase 2 (in progress) — Refinement: CRUD completeness (edit/delete across recipes, lists, weekly plan), correct unit-aware quantity aggregation, package-size-aware shopping quantities, Picnic match pricing
    • images, a "Weekly Plan" rename, and a design-polish pass. See docs/phases.md.
  • Phase 3 — Chat-assisted planning (previously Phase 2): not started. Open question: in-app chat vs. an MCP server exposing this app to external clients — deliberately undecided until Phase 2 is done.
  • Phase 4 — Learning & healthier suggestions (previously Phase 3): not started, deferred until there's real usage data. See the vision doc.

Running it

npm install
npm run dev

Copy .env.example to .env and fill in Picnic credentials if you want the basket integration (not required for recipes/lists).

Week planning with Claude (Phase 3 POC)

The landing page shows this week's training load and can have Claude plan next week's dinners around it — see docs/phases.md.

# 1. Add a Claude API key (console.anthropic.com)
echo 'ANTHROPIC_API_KEY=sk-ant-...' >> .env

# 2. Seed the starter recipe library (idempotent)
npx tsx scripts/seed-recipes.ts

# 3. Restart the dev server, then press "Plan next week with Claude"
npm run dev

Training data is a seeded demo week (src/lib/training.ts) so demos never depend on live OAuth; Strava slots in behind the same interface. Fuelling targets are computed deterministically in src/lib/fueling.ts — the model selects meals against them and never does the arithmetic — and every proposed plan is validated against real recipe ids before it is written (src/lib/plan-validation.ts).

Security notes

PICNIC_AUTH_KEY is a full-access bearer session token for a real Picnic account — not scoped to search/cart, not read-only. Anyone who obtains it can act as you against Picnic's API directly (no app login, password, or 2FA needed): browse, mutate the cart, and place a real order (cart.confirmOrder) charged to your account. Treat it as equivalent to an account password.

  • Keep it off any synced storage. iCloud Drive's "Desktop & Documents Folders" sync does not respect .gitignore — if this repo ever lives under a synced folder, .env leaves the laptop regardless of git config. Verify sync is off for this project's location (System Settings → Apple ID → iCloud → iCloud Drive), or move the repo out of a synced folder. (Resolved 2026-07-23 — repo moved from ~/Documents/Projects to ~/Developer, which iCloud Drive doesn't sync by default. Re-check this if the repo ever moves again.)
  • chmod 600 .env — owner-read/write only, not world-readable. (Already applied; re-apply if the file is ever recreated.)
  • Never run this app reachable off your laptop. next dev / next start bind to all interfaces by default — fine on localhost only. Don't expose this over LAN/Tailscale or deploy it anywhere until there's real auth in front of it. This matters more once any RPi/self-hosted deployment is on the table.
  • No automatable rotation. Getting a fresh key requires re-running the 2FA login flow (scripts/picnic-login.ts + scripts/picnic-verify-2fa.ts) by hand — there's no way to rotate this on a schedule. Only re-run it if you actually suspect the key leaked.
  • Real revocation lives on Picnic's side, not in this repo. This API wrapper has no "revoke this key" call. If you ever suspect a leak, log out other sessions or change your Picnic account password from the official app — that's what actually invalidates the old key.
  • See docs/BACKLOG.md for planned hardening (moving the key out of plaintext .env into macOS Keychain).

About

A nutrition goal goes in, a week of groceries turns up at your door. Reads Strava, plans dinners with Claude, pushes the whole shop to Picnic.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages