{"id":51525552,"url":"https://github.com/jacklvd/frondly-world","last_synced_at":"2026-07-08T21:01:06.382Z","repository":{"id":367851663,"uuid":"1281051940","full_name":"jacklvd/frondly-world","owner":"jacklvd","description":null,"archived":false,"fork":false,"pushed_at":"2026-07-05T18:47:00.000Z","size":20760,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-05T20:15:57.958Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jacklvd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-26T07:22:51.000Z","updated_at":"2026-07-05T18:47:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jacklvd/frondly-world","commit_stats":null,"previous_names":["jacklvd/frondly-world"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jacklvd/frondly-world","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Ffrondly-world","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Ffrondly-world/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Ffrondly-world/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Ffrondly-world/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jacklvd","download_url":"https://codeload.github.com/jacklvd/frondly-world/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Ffrondly-world/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35278166,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-08T02:00:06.796Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-08T21:01:05.500Z","updated_at":"2026-07-08T21:01:06.353Z","avatar_url":"https://github.com/jacklvd.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"client/assets/images/icon.png\" alt=\"Frondly logo\" width=\"120\" height=\"120\" /\u003e\n\n# 🌿 Frondly\n\n**Your friendly, AI-powered plant-care companion.**\n_Snap a photo → get an agent diagnosis \u0026 care plan → watch each plant thrive._\n\n\u003csub\u003e*Frondly* = **frond** (a leaf) + **friendly**\u003c/sub\u003e\n\n\u003cbr/\u003e\n\n[![Expo SDK](https://img.shields.io/badge/Expo-SDK%2056-000020?style=flat\u0026logo=expo\u0026logoColor=white)](https://docs.expo.dev/versions/v56.0.0/)\n[![React Native](https://img.shields.io/badge/React%20Native-0.85-61DAFB?style=flat\u0026logo=react\u0026logoColor=white)](https://reactnative.dev/)\n[![Python](https://img.shields.io/badge/Python-3.14+-3776AB?style=flat\u0026logo=python\u0026logoColor=white)](https://www.python.org/)\n[![FastAPI](https://img.shields.io/badge/FastAPI-0.138-009688?style=flat\u0026logo=fastapi\u0026logoColor=white)](https://fastapi.tiangolo.com/)\n[![Google ADK](https://img.shields.io/badge/Google%20ADK-2.3-4285F4?style=flat\u0026logo=google\u0026logoColor=white)](https://google.github.io/adk-docs/)\n[![Gemini](https://img.shields.io/badge/Gemini-2.5-8E75B2?style=flat\u0026logo=googlegemini\u0026logoColor=white)](https://ai.google.dev/)\n[![Firebase](https://img.shields.io/badge/Firebase-Auth%20+%20Firestore-FFCA28?style=flat\u0026logo=firebase\u0026logoColor=black)](https://firebase.google.com/)\n[![Tests](https://img.shields.io/badge/tests-89%20passing-3fb950?style=flat\u0026logo=jest\u0026logoColor=white)](#-testing)\n[![Kaggle Capstone](https://img.shields.io/badge/Kaggle-AI%20Agents%20Intensive%20Capstone-20BEFF?style=flat\u0026logo=kaggle\u0026logoColor=white)](https://www.kaggle.com/)\n[![Track](https://img.shields.io/badge/Track-Concierge%20Agents-6f42c1?style=flat)](#-kaggle-capstone-context)\n\n\u003c/div\u003e\n\n---\n\n## 🪴 The problem\n\nTwo everyday plant problems share one root cause — **not knowing what's wrong, when to act, or what's safe.**\n\n- **At home:** people kill houseplants not from neglect but from uncertainty — is it over- or under-watered? Is that spot a fungus or sunburn? How often should _this_ plant, in _this_ room, be watered? Generic care tags don't answer that, and plant-ID apps stop at a name.\n- **Outdoors:** foragers face a higher-stakes version of the same question — is this wild plant edible, or a toxic lookalike? Getting it wrong can be dangerous, and a bare species name is nowhere near enough to decide.\n\n## 💡 The solution\n\n**Frondly is a personal plant-care _and_ foraging concierge agent.** Point your camera at a plant and the agent:\n\n- **Identifies** it (name + species) and prefills your garden entry.\n- **Diagnoses** health from a photo, streaming back a plain-language explanation, a health score, and a care plan — and remembers the conversation so you can ask follow-ups.\n- **Schedules watering** per plant using species, room, light, and live local weather — not a fixed calendar.\n- **Forages safely** — identifies a plant in the wild against a research-verified PNW dataset, sorting it into **edible**, **not-edible** (recognized but not food), or **toxic** — with lookalike warnings, never a bare \"it's edible\", always paired with a safety reminder.\n- **Tracks** every plant and find on-device, with optional secure cloud backup.\n\n\u003e **Why an agent?** Plant care isn't one API call — it's *reasoning over messy visual input, then choosing the right tool*: identify vs. diagnose vs. compute a watering schedule vs. fetch weather. The `plantcare` ADK agent orchestrates those tools and holds context across a diagnosis conversation, which a static classifier can't do.\n\n## 🎓 Kaggle capstone context\n\nBuilt for the **Kaggle 5-Day AI Agents Intensive — Vibe Coding Capstone**, submitted to the **Concierge Agents** track (\"planning a garden … agents that keep personal information safe and secure\").\n\n**Course concepts demonstrated (≥3 required):**\n\n| Concept | How Frondly demonstrates it | Where |\n|---|---|---|\n| 🤖 **Agent system (ADK)** | `plantcare` ADK agent (Gemini 2.5) with tool-calling: diagnosis, watering schedule, weather, decline tracking; streamed over `/run_sse` | `server/plantcare/`, `server/main.py` |\n| 🔒 **Security features** | Firebase email/password auth + **per-user Firestore rules** (a user can only read/write their own backup); local-first — data lives on-device by default | `client/firebase/firestore.rules`, `client/src/lib/{auth.tsx,firebase.ts}` |\n| 🚀 **Deployability** | Stateless FastAPI backend (persists nothing) + Expo dev-build client; single `GEMINI_API_KEY`, no server DB to provision | `server/`, [Quickstart](#-quickstart) |\n\n## 🏗️ Architecture\n\nLocal-first mobile client + a **stateless** agent backend. The device is the source of truth; the server holds no user data.\n\n```mermaid\nflowchart TB\n    subgraph Device[\"📱 iPhone / Android — Expo Router + NativeWind\"]\n        UI[\"\u003cb\u003eUI routes\u003c/b\u003e\u003cbr/\u003eHome · Add · Forage · Care\u003cbr/\u003ePlant Detail · Diagnose · Account\"]\n        DB[(\"\u003cb\u003eWatermelonDB\u003c/b\u003e (SQLite)\u003cbr/\u003ePlant · Observation · Find\")]\n        WX[\"lib/weather\"]\n        API[\"lib/api · forage/api\u003cbr/\u003elib/config → API_BASE\"]\n        UI --\u003e DB\n        UI --\u003e WX\n        UI --\u003e API\n    end\n\n    subgraph Backend[\"🐍 FastAPI backend — server/ (stateless)\"]\n        ADK[\"\u003cb\u003eADK agent\u003c/b\u003e — /run · /run_sse · /sessions\u003cbr/\u003eplantcare (Gemini 2.5)\u003cbr/\u003etools: weather · decline · schedule · record_diagnosis\"]\n        EP[\"POST /forage/identify\u003cbr/\u003ePOST /plantcare/identify\u003cbr/\u003ePOST /plantcare/watering_schedule\"]\n    end\n\n    OM[\"🌤️ Open-Meteo\u003cbr/\u003e\u003ci\u003e(no key)\u003c/i\u003e\"]\n    FB[\"🔥 Firebase\u003cbr/\u003eAuth + Firestore\u003cbr/\u003e\u003ccode\u003ebackup/{uid}\u003c/code\u003e\"]\n    GEM[\"✨ Gemini 2.5\u003cbr/\u003evision + chat\"]\n\n    WX --\u003e|https| OM\n    API --\u003e|\"SSE / http · EXPO_PUBLIC_API_BASE\"| ADK\n    API --\u003e EP\n    UI -.-\u003e|sign-in · backup / restore| FB\n    ADK --\u003e GEM\n    EP --\u003e GEM\n\n    classDef device fill:#e8f5e9,stroke:#43a047,color:#1b5e20;\n    classDef backend fill:#e3f2fd,stroke:#1e88e5,color:#0d47a1;\n    classDef ext fill:#fff8e1,stroke:#f9a825,color:#e65100;\n    class UI,DB,WX,API device;\n    class ADK,EP backend;\n    class OM,FB,GEM ext;\n```\n\n## ✨ Features\n\n| Feature | What it does |\n|---|---|\n| 📸 **Plant identification** | Photo → name + species, prefilled into your garden (Gemini vision) |\n| 🩺 **Diagnose flow** | Photo → streamed agent diagnosis, health score, care plan; saved as an observation; ask follow-ups in a thread |\n| 💧 **Smart watering schedule** | Per-plant schedule from species + room + light + live weather; Care tab sorts soonest/overdue first |\n| 🌤️ **Live weather card** | Open-Meteo + device location (30-min cache), shown in °C and °F |\n| 🍃 **Forage Finds** | Identify plants in the wild against a 100+-entry verified PNW dataset (edible / not-edible / toxic), persist them, browse a species library |\n| 📖 **Garden journal** | Per-plant history, growth timeline, tappable note cards |\n| ☁️ **Secure cloud backup** | Firebase-auth-gated manual backup/restore of garden metadata to Firestore (per-user isolation) |\n\n## 🚀 Quickstart\n\nMonorepo: a Python agent backend (`server/`) and a React Native / Expo client (`client/`).\n\n### 1. Backend (`server/`)\n\nRequires [uv](https://docs.astral.sh/uv/) and a Google **`GEMINI_API_KEY`**.\n\n```bash\n# From the repo root — copy the template and add your Gemini key (loaded by server/main.py):\ncp .env.example .env                             # then edit .env and set GEMINI_API_KEY=...\n\ncd server\nuv sync\nuv run uvicorn main:app --reload                 # simulator → http://localhost:8000\n# For a physical device, bind all interfaces so the phone can reach it over LAN:\n# uv run uvicorn main:app --reload --host 0.0.0.0\n```\n\n### 2. Client (`client/`)\n\nRequires Node + [yarn](https://yarnpkg.com/). WatermelonDB needs native modules, so use an Expo **development build** (not Expo Go).\n\n```bash\ncd client\nyarn install\ncp .env.example .env.local             # then edit .env.local (see below)\n\n# Build \u0026 run on a connected physical device (prompts you to pick from your devices):\nnpx expo run:ios --device              # iOS — needs Xcode + a signing team; enable Developer Mode on the phone\nnpx expo run:android --device          # Android\n\n# …or build a dev client in the cloud via EAS (no local Xcode needed):\n# npx eas build --profile development --platform ios   # or android\n\nyarn start                             # Metro dev server (once the dev build is installed)\n```\n\n**Configure `client/.env.local`:**\n\n```bash\n# Backend URL — defaults to http://localhost:8000 (simulator + adb-reverse).\n# On a physical device, set your dev machine's LAN IP (find it with `ipconfig getifaddr en0`).\n# The IP below is only an example — replace it with YOUR Mac's IP:\nEXPO_PUBLIC_API_BASE=http://192.123.1.12:8000\n\n# Firebase (free Spark plan) — only needed for cloud backup/restore.\n# The app is fully usable signed-out; auth gates backup only.\nEXPO_PUBLIC_FIREBASE_API_KEY=...\nEXPO_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com\nEXPO_PUBLIC_FIREBASE_PROJECT_ID=your-project\nEXPO_PUBLIC_FIREBASE_APP_ID=...\n```\n\n### 3. (Optional) Enable cloud backup\n\nCreate a Firebase project (free Spark plan) → enable **Authentication ▸ Email/Password** → create a **Firestore Database** → paste the Web-app config into `client/.env.local` → publish the rules in `client/firebase/firestore.rules` (Firebase console ▸ Firestore ▸ Rules). No paid plan or Storage bucket required — backup is metadata-only.\n\n## 🧪 Testing\n\n```bash\ncd client \u0026\u0026 yarn jest --forceExit    # 89 tests (14 suites)\ncd server \u0026\u0026 uv run pytest            # backend tool + endpoint tests\n```\n\n## 🧰 Tech stack\n\n| Layer | Tech |\n|---|---|\n| **Agent backend** | Python 3.14, FastAPI, **Google ADK 2.3**, **Gemini 2.5** (vision + chat), uv |\n| **Client** | React Native 0.85 (Expo SDK 56), Expo Router, NativeWind (Tailwind v3), WatermelonDB (SQLite) |\n| **Cloud / auth** | Firebase Auth + Firestore (free Spark plan) |\n| **Data sources** | Open-Meteo (weather, no key) |\n| **Tooling** | uv (server); yarn + ESLint + Prettier + Husky + Jest (client) |\n\n## 📁 Repo structure\n\n```\nfrondly-world/\n├── server/    # FastAPI + Google ADK plantcare agent + Gemini vision (stateless)\n├── client/    # Expo / React Native app — Expo Router + NativeWind + WatermelonDB\n├── docs/      # Design specs, per-feature specs, and the project ROADMAP\n└── README.md\n```\n\nFeature-by-feature status and the full design history live in [`docs/ROADMAP.md`](docs/ROADMAP.md).\n\n## 📄 Notes\n\n- **No secrets in the repo** — `GEMINI_API_KEY` and Firebase keys are read from gitignored `.env` / `.env.local`; only `.env.example` is committed.\n- **Deployment is optional** for judging; this repo + the above setup instructions is the reproducible path. The backend is stateless and container-friendly if you do deploy it.\n\n## 🗺️ Next milestones\n\nFrondly is feature-complete for the capstone; these are the deliberate next steps to take it from \"portfolio project\" to \"daily driver.\"\n\n### 🔄 Sync \u0026 data\n- **Live multi-device sync** — today's cloud feature is *manual* backup/restore (whole-garden replace). True sync means WatermelonDB pull/push endpoints on a stateful backend, with conflict resolution and background push — a real architectural shift from today's stateless server.\n- **Auto-sync** — push changes on write (debounced) and pull on app-foreground, so a new phone or reinstall just works with no \"restore\" button.\n- **Cloud photo backup** — photos are device-local today (metadata-only backup). Add content-hash dedup + a blob store (e.g. paid Firebase Storage or an S3-compatible bucket) so images survive a reinstall.\n- **Backup history** — keep the last _N_ snapshots instead of overwriting one, enabling point-in-time restore.\n\n### 🔐 Auth \u0026 security\n- **OAuth / Google \u0026 Apple sign-in** — one-tap onboarding beyond email/password.\n- **Password reset + email verification** — table stakes for a real account system.\n- **Restore cleanup** — clear the temp/cache download dir after re-persisting photos into durable storage.\n\n### 🔔 Engagement\n- **Watering reminders** — local/push notifications driven by the per-plant schedule (\"Monstera is due today\").\n- **Diagnosis persistence** — the diagnose thread is ephemeral (`useState`); persist conversations so follow-ups survive leaving the screen.\n\n### 🚢 Platform \u0026 deploy\n- **Production EAS build** — an EAS profile that bakes a production `EXPO_PUBLIC_API_BASE`; publish to TestFlight / Play internal testing.\n- **Deploy the backend** — containerize the stateless FastAPI service to Cloud Run (or similar) with reproducible deploy docs.\n- **EAS project ownership** — settle which Expo account owns builds so non-Mac teammates can produce iOS builds.\n\u003c/content\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacklvd%2Ffrondly-world","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjacklvd%2Ffrondly-world","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacklvd%2Ffrondly-world/lists"}