Skip to content

Repository files navigation

Curiosity Coach

A weekly mood check-in that turns your curiosity into a personalized quest — a web app and a self-contained Chrome extension, sharing the same check-in → quest → feedback flow but with two independent backends.

How it works

  1. Check in — report your mood, energy, and progress, then pick up to 3 topics you're curious about (science, tech, space, art, nature, culture, music, history, economics, literature, philosophy, psychology).
  2. Get a quest — an LLM turns that into a short challenge: a title, a description with a concrete search query and journal prompt, a time estimate, and up to 3 recommended article/paper/video links. An assigned quest stays active for up to 7 days.
  3. Give feedback — rate it, mark it complete, optionally journal about it. This earns XP (base 100, +50 for completion, +25 for a rating ≥ 4, +30 for a journal entry) and updates your streak, and feeds into future prompts so quests don't repeat.
  4. Get reminded — the Chrome extension fires a weekly notification via chrome.alarms so the habit doesn't depend on remembering to open the app.

Two runtimes, one flow

CoachApi (src/lib/api/coach-api.ts) is the shared interface; which implementation backs it is picked by isExtensionRuntime() (VITE_COACH_RUNTIME env define):

  • Web app (coach-api.web.ts) — calls TanStack Start server functions (src/lib/api/*.functions.ts) → coach/service.server.ts, which builds a prompt (llm/prompt.server.ts, with per-topic niche hints so quests don't stay generic) and calls OpenAI gpt-4o-mini using the server-side OPENAI_API_KEY. Falls back to a static per-topic quest bank (lib/quests/fallback.ts) if the key is missing or the call fails.
  • Chrome extension (coach-api.extension.ts) — fully client-side. Messages the background service worker (extension/src/background.ts), which keeps profile/check-ins/quests/feedback in chrome.storage.sync and calls Groq's OpenAI-compatible API (llama-3.1-8b-instant) directly using a key the user pastes into the extension's own Settings page — never a server round-trip.

The web app has its own /settings route (src/settings.tsx); the extension has a separate options page (extension/src/settings.tsx, wired via options_page in the manifest) with a "test key" button and a "clear all data" reset. They're independent implementations, not shared code.

Tech stack

  • TanStack Start (React 19) with file-based routing — see src/routes/README.md for routing conventions
  • Vite 7, Tailwind v4, Radix UI primitives (shadcn-style components in src/components/ui)
  • TanStack Query, react-hook-form + zod
  • Chrome extension (Manifest V3), built as a separate Vite target (vite.extension.config.ts) with its own popup/settings/background entry points

Project structure

src/
  components/CuriosityCoach.tsx   # the check-in -> quest -> feedback UI (shared by both runtimes)
  settings.tsx                    # web app's /settings route (API-key UI for the web runtime)
  lib/
    coach/service.server.ts       # web path: check-in -> LLM prompt -> quest -> save
    llm/
      client.server.ts            # OpenAI call (gpt-4o-mini) for the web path
      prompt.server.ts            # prompt builder incl. per-topic niche hints
    quests/fallback.ts             # static per-topic quest bank (no-LLM fallback)
    api/
      coach-api.ts                 # shared CoachApi interface + isExtensionRuntime()
      coach-api.web.ts             # web implementation -> server functions
      coach-api.extension.ts       # extension implementation -> chrome.runtime messages
      *.functions.ts                # TanStack server functions (web path only)
    store/store.server.ts          # web path persistence
  types/coach.ts                  # Mood/Topic/Quest/UserProfile schemas (zod)
extension/
  manifest.json                  # MV3 manifest: popup + options_page + background worker
  popup.html / settings.html      # extension entry pages
  src/
    background.ts                # extension's own storage + Groq call + weekly alarm/notification
    popup.tsx                    # renders CuriosityCoach in extension runtime mode
    settings.tsx                 # extension options page: Groq key entry, test, clear data
scripts/prepare-extension.mjs    # copies manifest/icons into dist/extension after build

Setup

npm install

Web app — server-side env vars (create a .env, none are committed):

Variable Required Purpose
OPENAI_API_KEY No Enables LLM-generated quests for the web app. Without it, every quest comes from the fallback bank.
CORS_ORIGINS No Comma-separated allowed origins for the REST handler. Defaults to http://localhost:5173,http://127.0.0.1:5173.

Extension — no env vars; the user pastes their own Groq API key (free, from console.groq.com) into the extension's options page, stored in chrome.storage.sync.

Development

npm run dev        # web app (vite dev)
npm run build      # web app production build
npm run lint
npm run format      # prettier --write .

Chrome extension

npm run dev:extension    # watch build to dist/extension
npm run build:extension  # one-off build to dist/extension

Load dist/extension as an unpacked extension in Chrome (chrome://extensions → Developer mode → Load unpacked). Host permissions currently cover local dev ports (5173/3000) plus api.groq.com — update extension/manifest.json if the web app is deployed elsewhere.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages