Thanks for taking the time to contribute.
Luna is a soft, supportive menstrual cycle companion built with Next.js 15, Neon Postgres, and the Vercel AI SDK. Bug fixes, features, docs, ideas -- all welcome.
- Code of conduct
- Getting started
- Development setup
- Project architecture
- Coding standards
- Git workflow
- Pull requests
- Reporting bugs
- Feature requests
- Database changes
- AI tools
- Email templates
Be kind, respectful, and constructive. This is a health app -- be mindful that discussions may involve sensitive topics. We follow the Contributor Covenant code of conduct.
- Fork the repo
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/luna.git - Install dependencies:
pnpm install - Set up environment:
cp .env.example .envand fill in keys - Run the dev server:
pnpm dev
- Node.js 18+
- pnpm 9+ (
npm i -g pnpm) - Neon PostgreSQL database (free tier works)
- API keys for: HackClub AI, Supermemory, Resend (see
.env.example)
Copy .env.example to .env and fill in all required keys. Never commit .env files.
pnpm drizzle-kit generate # generate migration from schema changes
pnpm drizzle-kit migrate # apply migrationsThe schema lives in src/lib/db/schema.ts. If you modify it, always generate and commit the migration.
src/
├── app/ # Next.js App Router pages & API routes
│ ├── (app)/ # Authenticated pages (chat, dashboard, settings)
│ ├── (public)/ # Public pages (login, signup, landing)
│ └── api/ # API routes (auth, chat, data, user)
├── components/
│ ├── ai-elements/ # Chat UI primitives (Conversation, Message, Tool...)
│ └── ui/ # shadcn/ui components
└── lib/
├── chat/ # System prompt, OpenUI detection
├── cycle-tools.ts # 10 AI tools (logging, predictions, stats)
├── db/ # Drizzle schema, connection
├── email/ # Resend email templates
├── prediction/ # Adaptive exponential smoothing engine
└── theme/ # Plan-based accent colors
| Convention | Details |
|---|---|
| Chat API | Expects UIMessage parts (text/file), not content-only payloads |
| AI SDK v6 | maxSteps -> stopWhen: stepCountIs(n), usage.promptTokens -> usage.inputTokens |
| Drizzle | Use and(eq(...), eq(...)) -- chained .where().where() is invalid |
| OpenUI | Gate rendering with looksLikeOpenUiLang() (checks if text starts with root =) |
| Period length | Always inclusive: diffInDays(mStart, mEnd) + 1 (Jan 28-31 = 4 days) |
| Auth | Credentials provider only, JWT strategy -- no OAuth |
| Supermemory | Use v4 endpoints (POST /v4/search, POST /v4/memories) |
- Strict mode is enabled -- no
anytypes unless absolutely necessary - Use Zod for runtime validation (already a dependency)
- Prefer
interfacefor object types,typefor unions/intersections
- Tailwind v4 -- use utility classes, no custom CSS files unless necessary
- Color palette:
#6D5A60(mauve text),#8E7D82(dusk secondary),#FFB5C0(rose accent),#FFDDE0(blush border),#FFF9F9(cream bg) - Font:
font-seriffor headings (Instrument Serif),font-sansfor body (Figtree) - Border radius:
rounded-fullfor pills,rounded-2xlorrounded-3xlfor cards - Mobile-first: always design for mobile, then scale up with
md:andlg:breakpoints
- Use shadcn/ui components (
src/components/ui/) as building blocks - AI Elements (
src/components/ai-elements/) are for chat-specific UI - Keep components small and composable
- Framer Motion / Motion One for animations
- Use
useReducedMotion()hook for accessibility - Keep animations subtle -- 200-400ms, spring physics, slight transforms
main-- stable, deployedluna-rewrite-pub-- current development branch- Feature branches:
feat/your-feature-name - Fix branches:
fix/your-fix-name
Use Conventional Commits:
feat: add cycle phase notification
fix: period length off-by-one in dashboard
docs: update contributing guide
refactor: extract prediction engine to separate module
chore: update dependencies
- Use present tense ("add feature" not "added feature")
- Be specific -- "fix bug" is too vague; "fix period length showing 3 instead of 4" is good
- Create a branch from
luna-rewrite-pub - Make your changes with clear, atomic commits
- Test locally --
pnpm devand verify your changes work - Push to your fork
- Open a PR against
luna-rewrite-pub
## What does this PR do?
Brief description of changes.
## Type of change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
- [ ] Refactor
## How to test
Steps to verify the changes.
## Screenshots (if UI changes)
Before/after screenshots.
## Checklist
- [ ] I've read CONTRIBUTING.md
- [ ] My code follows the project's coding standards
- [ ] I've tested my changes locally
- [ ] I've updated relevant documentationOpen a GitHub Issue with:
- Steps to reproduce -- be specific
- Expected behavior -- what should happen
- Actual behavior -- what happens instead
- Screenshots if applicable
- Environment -- browser, device, OS
- Account details only if relevant (e.g., "17 cycles tracked, dashboard shows 6")
Open a GitHub Issue with the enhancement label:
- Problem -- what user pain point does this solve?
- Proposed solution -- how should it work?
- Alternatives considered -- what else did you think about?
- Modify
src/lib/db/schema.ts - Run
pnpm drizzle-kit generateto create a migration - Run
pnpm drizzle-kit migrateto apply locally - Commit both the schema change AND the generated migration SQL
- Document the change in your PR description
Never modify migration SQL files after they've been committed. Create a new migration instead.
Luna has 10 AI tools defined in src/lib/cycle-tools.ts:
| Tool | Purpose |
|---|---|
logPeriodStart |
Log period start date |
logPeriodEnd |
Log period end date |
logOvulation |
Log ovulation date |
addNoteSymptom |
Add free-text note or symptom |
fetchRecentCycles |
Fetch recent cycles (returns OpenUI table) |
computePredictions |
Next period/ovulation predictions |
fetchStats |
Cycle statistics and averages |
exportData |
Export user's cycle data |
rememberFact |
Store personal fact in Supermemory |
searchWeb |
Search the web via HackClub API |
When adding a new tool:
- Define it in
src/lib/cycle-tools.ts - Add it to the tool list in
src/app/api/chat/route.ts - Update the system prompt in
src/lib/chat/prompt.ts - Test with the chat interface
Email templates live in src/lib/email/index.ts:
- Logo: embedded as base64 data URI (not external URL -- email clients block those)
- Layout:
emailShell()wraps all emails with the Luna card design - Testing: use Resend's email preview or send to your own address
Reach out to Akshat Singh Kushwaha or open a GitHub Discussion.