Graspful is an agent-first adaptive learning platform. Courses are YAML files with knowledge graphs, imported via CLI or MCP server. The platform handles adaptive diagnostics, mastery-based progression, spaced repetition, white-label landing pages, and Stripe billing.
Contributions welcome: bug fixes, new features, course content, CLI commands, MCP tools, documentation improvements.
Prerequisites:
- Bun (package manager and runtime)
- Node.js 22+
- PostgreSQL (via Supabase local dev or remote instance)
Setup:
git clone https://github.com/graspful/graspful.git
cd graspful
bun install# Start all dev servers (Turborepo)
bun run dev
# Backend only (port 3000)
cd backend && bun run dev
# Frontend only (port 3001)
cd apps/web && bun run dev
# Build
bun run build
# Test (Jest for backend, Playwright for e2e)
bun run test
cd apps/web && npx playwright testPrisma (database):
cd backend
npx prisma migrate dev --name <migration-name>
npx prisma generategraspful/
├── apps/web/ # Next.js frontend (App Router, Tailwind, shadcn/ui)
├── backend/ # NestJS API (TypeScript, Prisma, PostgreSQL)
├── packages/
│ ├── shared/ # Zod schemas, types, quality gate
│ ├── cli/ # @graspful/cli (commander.js)
│ └── mcp/ # @graspful/mcp server for AI agent integration
├── content/
│ ├── courses/ # Course YAML files
│ ├── brands/ # Brand YAML files
│ └── academies/ # Multi-course academy manifests
├── docs/ # Documentation
└── supabase/ # Supabase config and migrations
The backend follows Domain-Driven Design with bounded contexts. Each NestJS module owns its domain.
| Context | Module | Owns |
|---|---|---|
| Knowledge Graph | knowledge-graph/ |
Courses, Concepts, KnowledgePoints, Edges |
| Student Model | student-model/ |
StudentProfile, ConceptState, KPState, mastery |
| Diagnostic | diagnostic/ |
BKT engine, MEPE selector, sessions |
| Learning Engine | learning-engine/ |
Task selection, frontier, remediation |
| Assessment | assessment/ |
Problems, answer evaluation, reviews |
| Spaced Repetition | spaced-repetition/ |
FIRe algorithm, review scheduling |
| Gamification | gamification/ |
XP, streaks, leaderboards |
Rules:
- Services call services, not repositories of other modules. If Diagnostic needs mastery data, it calls
StudentStateService, notprisma.studentConceptStatedirectly. - Controllers are thin. Extract params, validate input, delegate to service, return response. No domain logic in controllers.
- Each module owns its Prisma queries. Other modules request data through the owning module's service.
- Cross-context data is composed at the API/controller layer or in a dedicated query service -- not by having one domain service reach into another's tables.
See docs/adding-a-course.md for the full step-by-step guide.
Quick version:
- Scaffold:
graspful create course --topic "Your Topic" -o course.yaml - Fill concepts:
graspful fill concept course.yaml concept-id - Review:
graspful review course.yaml - Import:
graspful import course.yaml --org your-org --publish
Example courses live in content/courses/examples/ for reference.
The MCP server lives in packages/mcp/. It exposes tools for AI agents (Claude Code, Cursor, Codex) to create and manage courses programmatically.
To work on MCP tools:
- Edit tools in
packages/mcp/ - Test locally:
cd packages/mcp && bun run dev - Tools use the same Zod schemas from
packages/shared/
The CLI lives in packages/cli/ and uses commander.js.
To add a new command:
- Create a command file in
packages/cli/src/commands/ - Register it in the CLI entry point
- Use Zod schemas from
packages/shared/for validation - Test:
cd packages/cli && bun run dev -- <your-command>
- Database columns:
snake_case(Prisma@@map) - TypeScript:
camelCase - Package manager:
bun(not npm) - Schemas: Zod for all validation (shared across CLI, MCP, backend)
- Prisma models:
@db.Uuidfor IDs,@db.Timestamptzfor dates - Tests: Jest for backend unit tests, Playwright for e2e
- Keep PRs focused on a single change
- Include tests for new functionality
- Describe what changed and why in the PR description
- Run
bun run testbefore submitting - Follow the DDD boundary rules -- don't leak domain logic across modules
This project is licensed under the O'Saasy License -- MIT with one restriction: you can't offer this as a competing SaaS. By contributing, you agree that your contributions are licensed under the same terms.