Imported from adminstudentscoring/student-scoring-system (
AGENTS.md). Install upstream withnpx skills add adminstudentscoring/student-scoring-system. Copyright stays with the author.
AGENTS.md
Cursor Cloud specific instructions
Overview
This is StudentScoring — a chess education and student management platform built as a pnpm monorepo with Node.js 20.x / Express server (server.ts) and WebSocket support. The project uses pnpm workspaces.
Workspace packages:
- Root (
student-scoring-system): Express server entry point (server.ts). All logic is now in packages. @student-scoring/core(packages/core): Shared types, auth, middleware, storage, config, database, and lib utilities. The barrel export ispackages/core/src/index.ts.@student-scoring/platform(packages/platform): Auth, students, organizations, admin, attendance, chess.com teacher routes, autoRenew service, and OpenAI helper.@student-scoring/billing(packages/billing): PayPal billing, subscriptions, billing DB, access control, and billing/admin routes.@student-scoring/class-view(packages/class-view): Challenge, teacher class view, and statistics routes.@student-scoring/vcp(packages/vcp): V.Chess Platform WebSocket realtime module.- Application packages (browser-facing bundles live under repo root
application/; server modules underpackages/application/*; workspace globpackages/application/*inpnpm-workspace.yaml):@student-scoring/application-running-queen(packages/application/running-queen): Running Queen API routes.@student-scoring/application-royal-exchange(packages/application/royal-exchange): Royal Exchange API routes.@student-scoring/application-hope-mate(packages/application/hope-mate): Hope Mate + admin stage-puzzle routes.@student-scoring/application-chess(packages/application/chess): Chess Light, Chess Solitaire, Chess Works, Maze Runner routes.@student-scoring/application-monster-fight(packages/application/monster-fight): Monster Fight game logic, routes, and leaderboard (HTTP API remains under/api/game/*).@student-scoring/application-blunders(packages/application/blunders): Blunders analysis, Chess.com sync, Stockfish integration, teacher/public routes, and DB helpers.@student-scoring/application-tactics-fighter(packages/application/tactics-fighter): Tactics Fighter builder, puzzles, attempts, and admin routes.@student-scoring/application-truceboard(packages/application/truceboard): Registers/truceboardstatic fromapplication/truceboard/.
Prerequisites
- Node.js 20.x (required by
enginesfield inpackage.json). Usenvm use 20if multiple versions are installed. - pnpm as the package manager (workspace root has
pnpm-workspace.yaml). Runpnpm install(notnpm install). - PostgreSQL 16 must be running locally. The billing module (
billing/db.ts) callscreatePool()at require-time and will crash the server ifDATABASE_URLis absent or the DB is unreachable. - A
.envfile (copy fromenv.example). At minimum, set:DATABASE_URL=postgres://studentscoring:studentscoring@localhost:5432/studentscoringPGSSLMODE=disablePAYPAL_CLIENT_ID,PAYPAL_CLIENT_SECRET,PAYPAL_WEBHOOK_ID— use any non-empty placeholder strings for dev (e.g.sb-dev-placeholder); the module validates presence at load time but the sandbox endpoints are only hit when billing features are exercised.DB_AUTO_MIGRATE=1to auto-run Postgres migrations on startup.
Running the application
pnpm dev # runs tsx watch server.ts on port 7001 (override with PORT)
# or: pnpm start (npx tsx server.ts)
Server listens on http://localhost:7001 by default (set PORT in .env to override). The Teacher Dashboard is at /, login at /login.html, admin at /admin.html, organization management at /organization.html.
Monorepo structure
pnpm-workspace.yaml # declares packages/*, packages/application/*, and apps/*
packages/core/ # @student-scoring/core — shared auth, types, middleware, storage, config, db, lib
packages/platform/ # @student-scoring/platform — auth, students, organizations, admin, attendance routes + autoRenew + OpenAI
packages/billing/ # @student-scoring/billing — PayPal billing, subscriptions, access control, billing routes
packages/class-view/ # @student-scoring/class-view — challenge, teacher class view, statistics routes
packages/vcp/ # @student-scoring/vcp — V.Chess Platform WebSocket
packages/application/* # one workspace package per application family (see list above)
application/ # static assets served at /application/... (HTML, JS, CSS, images); shared shell: application-window.html
server.ts # main Express server (root workspace) — imports ONLY from @student-scoring/* packages
Import from packages in server.ts:
const { authenticateUser, LEVELS, getRankInfo } = require('@student-scoring/core');
const { registerAuthRoutes, registerOrganizationsRoutes, createAutoRenew, openAiEnabled } = require('@student-scoring/platform');
const { registerPayPalRoutes, registerOrganizationsBillingRoutes, createPayPalBillingHelpers } = require('@student-scoring/billing');
const { registerChallengeRoutes, registerStatisticsRoutes } = require('@student-scoring/class-view');
const { setupVcpChess } = require('@student-scoring/vcp');
const { registerRunningQueenRoutes } = require('@student-scoring/application-running-queen');
const { registerRoyalExchangeRoutes } = require('@student-scoring/application-royal-exchange');
const { registerHopeMateRoutes, registerHopeMateAdminRoutes } = require('@student-scoring/application-hope-mate');
const { registerChessLightRoutes } = require('@student-scoring/application-chess');
const { registerMonsterFightRoutes } = require('@student-scoring/application-monster-fight');
const { registerBlundersTeacherRoutes, createBlundersStorage } = require('@student-scoring/application-blunders');
const { registerTacticsFighterRoutes } = require('@student-scoring/application-tactics-fighter');
const { registerTruceboardRoutes } = require('@student-scoring/application-truceboard');
For submodules not re-exported by the barrel (e.g. direct db/billing access):
const appDb = require('@student-scoring/core/src/db/postgres');
const billingDb = require('@student-scoring/billing/src/db');
const paypal = require('@student-scoring/billing/src/paypal');
Gotchas
- ESLint is configured via flat config (
eslint.config.js, ESLint 10+) for both.jsand.tsfiles.@typescript-eslintis used for TypeScript-specific rules. Runpnpm lint. Most rules are set to warn. For type checking.tsfiles, also runpnpm typecheck(tsc --noEmit). - Integration tests use Node.js built-in test runner + supertest. Run
pnpm test. Tests hit a running server onhttp://localhost:7001by default (TEST_BASE_URLoverrides), so start the server first or the test helper will spawn one automatically. GET /api/studentsusesoptionalAuthand returns 200 without a token — usePOST /api/studentsto test auth-required student routes.- The server uses file-based storage (
data/directory) for most entities (students, users, organizations, leaderboards). PostgreSQL is used for billing, blunders, tactics fighter, and migrations. billing/paypal.tsrequiresPAYPAL_CLIENT_ID,PAYPAL_CLIENT_SECRET, andPAYPAL_WEBHOOK_IDas environment variables at module load time. The server will crash without them.- The
data/directory is auto-created by the server on startup viaensureDataDir(). - pnpm build scripts:
bcrypt,sharp,esbuild, andelectronare allowed to run build scripts viapnpm.onlyBuiltDependenciesin rootpackage.json. If adding new native deps, add them there. - The
middleware/auth.tsandmiddleware/dataIsolation.ts(inpackages/core/src/) import@student-scoring/billing/src/access. This creates a runtime circular dependency (core ↔ billing), which works because of Node.js CJS module caching but is a known code smell. - SQL migrations live in
packages/core/src/db/migrations/. Thedb/migrate.tsmodule uses__dirnameto find them.
Useful scripts (see package.json)
| Script | Purpose |
|---|---|
pnpm dev / pnpm start |
Start the server |
pnpm init-admin |
Create the first admin user |
pnpm db:migrate |
Run Postgres migrations manually |
pnpm db:ping |
Test Postgres connectivity |
pnpm lint |
Run ESLint on .js and .ts files (flat config, warnings + TS rules) |
pnpm typecheck |
Run tsc --noEmit on .ts files |
pnpm test |
Run integration tests (server must be running) |