run-meal-advice
Run Gram meal tracker with hot-reload development servers (backend :8000, frontend :5173)
Files2
View on GitHubSKILL.md.claude/skills/run-meal-advice/SKILL.md
GitHubGram Meal Tracker Dev Environment
Gram is a self-hosted meal/calorie tracker: Svelte SPA frontend (Tailwind + DaisyUI) + FastAPI backend + SQLite, runs on Raspberry Pi in production.
In development: both servers run with hot reload — backend FastAPI on :8000, frontend Vite on :5173. Agent interaction uses chromium-cli against the frontend; backend is testable via curl.
Prerequisites
Ubuntu/Debian packages:
apt-get update && apt-get install -y python3 python3-venv python3-pip nodejs npm
Paths in this guide are relative to the project root.
Build
The driver handles all setup. First run takes 30-60 seconds (deps, venv setup):
./.claude/skills/run-meal-advice/driver.sh start
This does:
- Creates Python venv if missing
- Installs
backend/requirements.txt(FastAPI, uvicorn, pydantic) - Installs
frontend/package.json(Vite, Svelte, Tailwind, DaisyUI) - Starts backend uvicorn on
:8000with--reload - Starts frontend Vite dev server on
:5173 - Waits for both to be healthy (backend:
/api/health, frontend: HTTP 200)
Run (agent path)
Launch
./.claude/skills/run-meal-advice/driver.sh start
Outputs logs to:
/tmp/gram-backend.log— backend startup, errors, warnings/tmp/gram-frontend.log— Vite compilation, HMR events
Interact with frontend
Use chromium-cli to drive the SPA:
chromium-cli http://localhost:5173/ <<'SCRIPT'
navigate http://localhost:5173/
screenshot gram-app.png
# Add a food
click "input[placeholder*='food']"
type "Apple"
click "button:has-text('Add')"
screenshot after-add.png
wait "text=Apple"
SCRIPT
Test backend API
Direct curl calls (no browser needed):
# Health check
curl http://localhost:8000/api/health
# Add a food (per-100g nutrition values)
curl -X POST http://localhost:8000/api/foods \
-H "Content-Type: application/json" \
-d '{
"name": "Banana",
"calories": 89,
"protein": 1.1,
"carbohydrate": 23,
"fat": 0.3
}'
# Log an entry (amount_grams × nutrition = daily totals)
curl -X POST http://localhost:8000/api/entries \
-H "Content-Type: application/json" \
-d '{
"food_id": 1,
"amount_grams": 100,
"meal": "breakfast",
"consumed_date": "2026-06-26"
}'
# Today's totals
curl http://localhost:8000/api/today/summary
# Search foods
curl "http://localhost:8000/api/foods?q=apple"
# See all endpoints
curl http://localhost:8000/docs # Swagger UI
Stop
./.claude/skills/run-meal-advice/driver.sh stop
Check status
./.claude/skills/run-meal-advice/driver.sh status
Run (human path)
Terminal 1 — backend:
python3 -m venv .venv && ./.venv/bin/pip install -r backend/requirements.txt
./.venv/bin/uvicorn backend.app.main:app --reload # :8000
Terminal 2 — frontend:
cd frontend
npm install
npm run dev # :5173, proxies /api → :8000
Open http://localhost:5173 in browser.
Direct API Testing (no frontend)
Useful for PRs touching only backend logic:
# Start just the backend
./.venv/bin/uvicorn backend.app.main:app --reload
# In another shell, run test queries (see "Test backend API" above)
Gotchas
Port conflicts: If :8000 or :5173 are in use, driver fails silently (curl timeout). Kill conflicting processes or use lsof -i :8000 / lsof -i :5173 to find them.
Stale node_modules: If frontend/package-lock.json changes but node_modules is missing, npm install runs fresh but Vite may serve old code. The driver always runs npm install, so this is rare.
SQLite location: By default, meals.db lives at /tmp/meals.db (see backend/app/config.py MEAL_DB env var). Each fresh start has a clean database — no persistent data between runs unless you move the .db file.
API field names: Nutrition fields on POST are not _per_100g suffixed — they're calories, protein, carbohydrate, fat (and optional: saturated_fat, sugar, fiber, salt). The database stores per-100g values; the entry POST body multiplies by amount_grams.
Vite HMR: Frontend changes hot-reload instantly. Backend changes restart the uvicorn process automatically (triggered by file watch on backend/).
Troubleshooting
| Symptom | Fix |
|---|---|
error: connect ECONNREFUSED 127.0.0.1:8000 |
Backend failed to start. Check /tmp/gram-backend.log for errors. Usually: missing Python deps or port in use. |
curl: (7) Failed to connect to localhost:5173 |
Frontend failed to start. Check /tmp/gram-frontend.log. Usually: missing npm deps or Node/npm version issue. |
uvicorn: command not found |
Python venv not activated. Driver handles this; if running manually, use ./.venv/bin/uvicorn. |
npm: command not found |
Node/npm not installed. Run apt-get install -y nodejs npm (or homebrew on macOS). |
| All blank page in browser, console errors | SQLite perms or corrupted .db file. Clear /tmp/meals.db and restart. |
| Stale frontend code after git checkout | Hard-reset Vite cache: rm -rf frontend/node_modules/.vite then restart driver. |
| Playwright sees old code despite page reload | Service worker serves stale assets. Unregister it first: await page.evaluate(() => navigator.serviceWorker.getRegistrations().then(regs => Promise.all(regs.map(r => r.unregister())))) then reload. Do this once at the start of each Playwright session. |