Imported from halomartha-coder/halo-katamochi (
AGENTS.md). Install upstream withnpx skills add halomartha-coder/halo-katamochi. Copyright stays with the author.
AGENTS.md
Indonesian self-serve digital wedding invitation SaaS. Customers browse
themes, order and pay (mock provider), edit their invite in a dashboard,
publish, and share /i/<slug> with guests who submit RSVPs. Built with
Next.js 15 App Router + TypeScript + Prisma/SQLite + a homegrown cookie
session - no external auth/payment provider, no test suite (out of spec
scope).
Skills index
| Skill | Path | Load when |
|---|---|---|
| dev-environment | .agents/skills/dev-environment/SKILL.md |
Setting up, running, building, or debugging the dev server; env vars; ports; db scripts |
| database-prisma | .agents/skills/database-prisma/SKILL.md |
Editing prisma/schema.prisma, writing Prisma queries, running db:push/seed/reset, MySQL migration |
| auth-and-session-testing | .agents/skills/auth-and-session-testing/SKILL.md |
Login/session/middleware work, or manually testing a protected route via curl |
| invite-renderer | .agents/skills/invite-renderer/SKILL.md |
The story carousel, parallax, tap navigation, theme tokens, anything under components/invite/ or the carousel hooks |
| invite-content-and-plans | .agents/skills/invite-content-and-plans/SKILL.md |
Changing InviteConfig, the dashboard editor, shared copy, or plan feature gates |
| orders-and-payments | .agents/skills/orders-and-payments/SKILL.md |
Checkout flow, mock payment provider, adding a real PaymentProvider |
| adding-a-theme | .agents/skills/adding-a-theme/SKILL.md |
Adding a 4th invitation theme |
These are split one-per-concern rather than merged, because each maps to a distinct, independently-touchable slice of the codebase (routes vs. schema vs. carousel hooks vs. pricing) - merging any two would force an agent to load unrelated context it doesn't need for a given task.
Tribal knowledge
- Next.js silently falls back to the next free port when 3000 is
taken (
next dev/next startboth logPort 3000 is in use ..., using available port <n> instead) - always confirm the actual port from the log before hitting it with curl. - Never run
next devandnext startagainst the same.nextdirectory concurrently - they corrupt each other's build manifests (ENOENT routes-manifest.json,Cannot find module middleware-manifest.json). Stop one before starting the other. middleware.tsis Edge-only: it verifies the session JWT withjoseand nothing else. It must never import Prisma orbcryptjs. Real role checks (ADMIN) and invite-ownership checks happen in the page/route itself, backed by a DB read - a non-admin authenticated user hitting/admingets a 404 (app/admin/layout.tsx), not a redirect or 403.- Prisma schema has no native enums by design (MySQL portability): every
"enum" column is a
String+ a TS union/const array in the matchinglib/*.tsfile (see database-prisma skill). Follow that pattern for any new enum-like column. - Server-side price/gate enforcement is the actual authority everywhere
a client could lie: order pricing (
lib/pricing.tslookup, never a client-sent amount) and plan gates (gallery limit, music, custom slug) are re-checked in the PATCH invite route regardless of what the editor UI already disabled. prisma/seed.tsis fully idempotent (upserteverywhere) - safe to rerun withnpm run db:seed; onlynpm run db:resetactually wipes data.
Token-cheap commands
# lint - just the status line
npm run lint 2>&1 | tail -3
# build - compile status only
npm run build 2>&1 | grep -E "Compiled successfully|Failed to compile"
# confirm which port dev landed on, then hit it
npm run dev &
sleep 4 && tail -5 /tmp/devA.log 2>/dev/null # or watch the terminal output directly
# HTTP status codes only (no body)
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/dashboard # 307 when logged out
# login + reuse session cookie against a protected route
rm -f /tmp/cookies.txt
curl -s -c /tmp/cookies.txt -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"customer@undangan.dev","password":"customer12345"}'
curl -s -b /tmp/cookies.txt -o /dev/null -w '%{http_code}\n' http://localhost:3000/dashboard
# quick row counts, no Node/Prisma client needed
sqlite3 prisma/dev.db "select count(*) from Invite;"
sqlite3 prisma/dev.db "select count(*) from Rsvp;"
sqlite3 prisma/dev.db "select email, role from User;"
# prisma schema sanity check (no build needed)
npx prisma validate